aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/vc/diff-mode.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/vc/diff-mode.el')
-rw-r--r--lisp/vc/diff-mode.el170
1 files changed, 111 insertions, 59 deletions
diff --git a/lisp/vc/diff-mode.el b/lisp/vc/diff-mode.el
index 5c0fb5fba4c..559310ff770 100644
--- a/lisp/vc/diff-mode.el
+++ b/lisp/vc/diff-mode.el
@@ -201,8 +201,9 @@ The default \"-b\" means to ignore whitespace-only changes,
201(defvar-keymap diff-mode-shared-map 201(defvar-keymap diff-mode-shared-map
202 :doc "Bindings for read-only `diff-mode' buffers. 202 :doc "Bindings for read-only `diff-mode' buffers.
203These bindings are also available with an ESC prefix 203These bindings are also available with an ESC prefix
204(i.e. a \\=`M-' prefix) in read-write `diff-mode' buffers, 204(i.e. a \\=`M-' prefix) in all `diff-mode' buffers, including in
205and with a `diff-minor-mode-prefix' prefix in `diff-minor-mode'. 205particular read-write `diff-mode' buffers, and with a
206`diff-minor-mode-prefix' prefix in `diff-minor-mode'.
206See also `diff-mode-read-only-map'." 207See also `diff-mode-read-only-map'."
207 "n" #'diff-hunk-next 208 "n" #'diff-hunk-next
208 "N" #'diff-file-next 209 "N" #'diff-file-next
@@ -217,14 +218,7 @@ See also `diff-mode-read-only-map'."
217 "RET" #'diff-goto-source 218 "RET" #'diff-goto-source
218 "<mouse-2>" #'diff-goto-source 219 "<mouse-2>" #'diff-goto-source
219 "o" #'diff-goto-source ; other-window 220 "o" #'diff-goto-source ; other-window
220 "<remap> <undo>" #'undo-ignore-read-only 221 "<remap> <undo>" #'undo-ignore-read-only)
221
222 ;; The foregoing commands don't affect buffers beyond this one.
223 ;; The following command is the only one that has a single-letter
224 ;; binding and which affects buffers beyond this one.
225 ;; However, the following command asks for confirmation by default,
226 ;; so that seems okay. --spwhitton
227 "u" #'diff-revert-and-kill-hunk)
228 222
229;; Not `diff-read-only-mode-map' because there is no such mode 223;; Not `diff-read-only-mode-map' because there is no such mode
230;; `diff-read-only-mode'; see comment above. 224;; `diff-read-only-mode'; see comment above.
@@ -233,15 +227,28 @@ See also `diff-mode-read-only-map'."
233 :doc "Additional bindings for read-only `diff-mode' buffers. 227 :doc "Additional bindings for read-only `diff-mode' buffers.
234Most of the bindings for read-only `diff-mode' buffers are in 228Most of the bindings for read-only `diff-mode' buffers are in
235`diff-mode-shared-map'. This map contains additional bindings for 229`diff-mode-shared-map'. This map contains additional bindings for
236read-only `diff-mode' buffers that are *not* available with an ESC 230read-only `diff-mode' buffers that are *not* also available with an ESC
237prefix (i.e. a \\=`M-' prefix) in read-write `diff-mode' buffers." 231prefix (i.e. a \\=`M-' prefix) in read-write (nor read-only) `diff-mode'
232buffers."
238 ;; We don't want the following in read-write `diff-mode' buffers 233 ;; We don't want the following in read-write `diff-mode' buffers
239 ;; because they hide useful `M-<foo>' global bindings when editing. 234 ;; because they hide useful `M-<foo>' global bindings when editing.
240 "W" #'widen 235 "W" #'widen
241 "w" #'diff-kill-ring-save 236 "w" #'diff-kill-ring-save
242 "A" #'diff-ediff-patch 237 "A" #'diff-ediff-patch
243 "r" #'diff-restrict-view 238 "r" #'diff-restrict-view
244 "R" #'diff-reverse-direction) 239 "R" #'diff-reverse-direction
240 "s" #'diff-split-hunk
241
242 ;; The foregoing commands in `diff-mode-shared-map' and
243 ;; `diff-mode-read-only-map' don't affect buffers beyond this one.
244 ;; The following command is the only one that has a single-character
245 ;; binding and which affects buffers beyond this one. However, the
246 ;; following command asks for confirmation by default, so that seems
247 ;; okay. --spwhitton
248 "u" #'diff-revert-and-kill-hunk
249 ;; `diff-revert-and-kill-hunk' is the `diff-mode' analogue of what '@'
250 ;; does in VC-Dir, so give it the same short binding.
251 "@" #'diff-revert-and-kill-hunk)
245 252
246(defvar-keymap diff-mode-map 253(defvar-keymap diff-mode-map
247 :doc "Keymap for `diff-mode'. See also `diff-mode-shared-map'." 254 :doc "Keymap for `diff-mode'. See also `diff-mode-shared-map'."
@@ -882,31 +889,19 @@ If the prefix ARG is given, restrict the view to the current file instead."
882 (goto-char (point-min)) 889 (goto-char (point-min))
883 (re-search-forward diff-hunk-header-re nil t))) 890 (re-search-forward diff-hunk-header-re nil t)))
884 891
885(defun diff-hunk-kill () 892(defun diff-hunk-kill (&optional beg end)
886 "Kill the hunk at point." 893 "Kill the hunk at point.
887 (interactive) 894When killing the last hunk left for a file, kill the file header too.
888 (if (not (diff--some-hunks-p)) 895Interactively, if the region is active, kill all hunks that the region
889 (error "No hunks") 896overlaps.
890 (diff-beginning-of-hunk t) 897
891 (let* ((hunk-bounds (diff-bounds-of-hunk)) 898When called from Lisp with optional arguments BEG and END non-nil, kill
892 (file-bounds (ignore-errors (diff-bounds-of-file))) 899all hunks overlapped by the region from BEG to END as though called
893 ;; If the current hunk is the only one for its file, kill the 900interactively with an active region delimited by BEG and END."
894 ;; file header too. 901 (interactive "R")
895 (bounds (if (and file-bounds 902 (when (xor beg end)
896 (progn (goto-char (car file-bounds)) 903 (error "Invalid call to `diff-hunk-kill'"))
897 (= (progn (diff-hunk-next) (point)) 904 (diff--revert-kill-hunks beg end nil))
898 (car hunk-bounds)))
899 (progn (goto-char (cadr hunk-bounds))
900 ;; bzr puts a newline after the last hunk.
901 (while (looking-at "^\n")
902 (forward-char 1))
903 (= (point) (cadr file-bounds))))
904 file-bounds
905 hunk-bounds))
906 (inhibit-read-only t))
907 (apply #'kill-region bounds)
908 (goto-char (car bounds))
909 (ignore-errors (diff-beginning-of-hunk t)))))
910 905
911;; This is not `diff-kill-other-hunks' because we might need to make 906;; This is not `diff-kill-other-hunks' because we might need to make
912;; copies of file headers in order to ensure the new kill ring entry 907;; copies of file headers in order to ensure the new kill ring entry
@@ -2282,6 +2277,83 @@ With a prefix argument, try to REVERSE the hunk."
2282 :type 'boolean 2277 :type 'boolean
2283 :version "31.1") 2278 :version "31.1")
2284 2279
2280(defun diff--revert-kill-hunks (beg end revertp)
2281 "Workhorse routine for killing hunks, after possibly reverting them.
2282If BEG and END are nil, kill the hunk at point.
2283Otherwise kill all hunks overlapped by region delimited by BEG and END.
2284When killing a hunk that's the only one remaining for its file, kill the
2285file header too.
2286If REVERTP is non-nil, reverse-apply hunks before killing them."
2287 ;; With BEG and END non-nil, we push each hunk to the kill ring
2288 ;; separately. If we want to push to the kill ring just once, we have
2289 ;; to decide how to handle file headers such that the meanings of the
2290 ;; hunks in the kill ring entry, considered as a whole patch, do not
2291 ;; deviate too far from the meanings the hunks had in this buffer.
2292 ;;
2293 ;; For example, if we have a single hunk for one file followed by
2294 ;; multiple hunks for another file, and we naïvely kill the single
2295 ;; hunk and the first of the multiple hunks, our kill ring entry will
2296 ;; be a patch applying those two hunks to the first file. This is
2297 ;; because killing the single hunk will have brought its file header
2298 ;; with it, but not so killing the second hunk. So we will have put
2299 ;; together hunks that were previously for two different files.
2300 ;;
2301 ;; One option is to *copy* every file header that the region overlaps
2302 ;; (and that we will not kill, because we are leaving other hunks for
2303 ;; that file behind). But then the text this command pushes to the
2304 ;; kill ring would be different from the text it removes from the
2305 ;; buffer, which would be unintuitive for an Emacs kill command.
2306 ;;
2307 ;; An alternative might be to have restrictions as follows:
2308 ;;
2309 ;; Interactively, if the region is active, try to kill all hunks that the
2310 ;; region overlaps. This works when either
2311 ;; - all the hunks the region overlaps are for the same file; or
2312 ;; - the last hunk the region overlaps is the last hunk for its file.
2313 ;; These restrictions are so that the text added to the kill ring does not
2314 ;; merge together hunks for different files under a single file header.
2315 ;;
2316 ;; We would error out if neither property is met. When either holds,
2317 ;; any file headers the region overlaps are ones we should kill.
2318 (unless (diff--some-hunks-p)
2319 (error "No hunks"))
2320 (if beg
2321 (save-excursion
2322 (goto-char beg)
2323 (setq beg (car (diff-bounds-of-hunk)))
2324 (goto-char end)
2325 (unless (looking-at diff-hunk-header-re)
2326 (setq end (cadr (diff-bounds-of-hunk)))))
2327 (pcase-setq `(,beg ,end) (diff-bounds-of-hunk)))
2328 (when (or (not revertp) (null (diff-apply-buffer beg end t)))
2329 (goto-char end)
2330 (when-let* ((pos (diff--at-diff-header-p)))
2331 (goto-char pos))
2332 (setq beg (copy-marker beg) end (point-marker))
2333 (unwind-protect
2334 (cl-loop initially (goto-char beg)
2335 with inhibit-read-only = t
2336 for (hunk-beg hunk-end) = (diff-bounds-of-hunk)
2337 for file-bounds = (ignore-errors (diff-bounds-of-file))
2338 for (file-beg file-end) = file-bounds
2339 if (and file-bounds
2340 (progn
2341 (goto-char file-beg)
2342 (diff-hunk-next)
2343 (eq (point) hunk-beg))
2344 (progn
2345 (goto-char hunk-end)
2346 ;; bzr puts a newline after the last hunk.
2347 (while (looking-at "^\n") (forward-char 1))
2348 (eq (point) file-end)))
2349 do (kill-region file-beg file-end) (goto-char file-beg)
2350 else do (kill-region hunk-beg hunk-end) (goto-char hunk-beg)
2351 do (ignore-errors (diff-beginning-of-hunk t))
2352 until (or (< (point) (marker-position beg))
2353 (eql (point) (marker-position end))))
2354 (set-marker beg nil)
2355 (set-marker end nil))))
2356
2285(defun diff-revert-and-kill-hunk (&optional beg end) 2357(defun diff-revert-and-kill-hunk (&optional beg end)
2286 "Reverse-apply and then kill the hunk at point. Save changed buffer. 2358 "Reverse-apply and then kill the hunk at point. Save changed buffer.
2287Interactively, if the region is active, reverse-apply and kill all 2359Interactively, if the region is active, reverse-apply and kill all
@@ -2307,27 +2379,7 @@ BEG and END."
2307 (error "Invalid call to `diff-revert-and-kill-hunk'")) 2379 (error "Invalid call to `diff-revert-and-kill-hunk'"))
2308 (when (or (not diff-ask-before-revert-and-kill-hunk) 2380 (when (or (not diff-ask-before-revert-and-kill-hunk)
2309 (y-or-n-p "Really reverse-apply and kill hunk(s)?")) 2381 (y-or-n-p "Really reverse-apply and kill hunk(s)?"))
2310 (if beg 2382 (diff--revert-kill-hunks beg end t)))
2311 (save-excursion
2312 (goto-char beg)
2313 (setq beg (car (diff-bounds-of-hunk)))
2314 (goto-char end)
2315 (unless (looking-at diff-hunk-header-re)
2316 (setq end (cadr (diff-bounds-of-hunk)))))
2317 (pcase-setq `(,beg ,end) (diff-bounds-of-hunk)))
2318 (when (null (diff-apply-buffer beg end t))
2319 ;; Use `diff-hunk-kill' because it properly handles file headers.
2320 (goto-char end)
2321 (when-let* ((pos (diff--at-diff-header-p)))
2322 (goto-char pos))
2323 (setq beg (copy-marker beg) end (point-marker))
2324 (unwind-protect
2325 (cl-loop initially (goto-char beg)
2326 do (diff-hunk-kill)
2327 until (or (< (point) (marker-position beg))
2328 (eql (point) (marker-position end))))
2329 (set-marker beg nil)
2330 (set-marker end nil)))))
2331 2383
2332(defun diff-apply-buffer (&optional beg end reverse test-or-no-save) 2384(defun diff-apply-buffer (&optional beg end reverse test-or-no-save)
2333 "Apply the diff in the entire diff buffer. 2385 "Apply the diff in the entire diff buffer.