diff options
Diffstat (limited to 'lisp/vc/diff-mode.el')
| -rw-r--r-- | lisp/vc/diff-mode.el | 170 |
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. |
| 203 | These bindings are also available with an ESC prefix | 203 | These 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 |
| 205 | and with a `diff-minor-mode-prefix' prefix in `diff-minor-mode'. | 205 | particular read-write `diff-mode' buffers, and with a |
| 206 | `diff-minor-mode-prefix' prefix in `diff-minor-mode'. | ||
| 206 | See also `diff-mode-read-only-map'." | 207 | See 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. |
| 234 | Most of the bindings for read-only `diff-mode' buffers are in | 228 | Most 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 |
| 236 | read-only `diff-mode' buffers that are *not* available with an ESC | 230 | read-only `diff-mode' buffers that are *not* also available with an ESC |
| 237 | prefix (i.e. a \\=`M-' prefix) in read-write `diff-mode' buffers." | 231 | prefix (i.e. a \\=`M-' prefix) in read-write (nor read-only) `diff-mode' |
| 232 | buffers." | ||
| 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) | 894 | When killing the last hunk left for a file, kill the file header too. |
| 888 | (if (not (diff--some-hunks-p)) | 895 | Interactively, if the region is active, kill all hunks that the region |
| 889 | (error "No hunks") | 896 | overlaps. |
| 890 | (diff-beginning-of-hunk t) | 897 | |
| 891 | (let* ((hunk-bounds (diff-bounds-of-hunk)) | 898 | When called from Lisp with optional arguments BEG and END non-nil, kill |
| 892 | (file-bounds (ignore-errors (diff-bounds-of-file))) | 899 | all 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 | 900 | interactively 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. | ||
| 2282 | If BEG and END are nil, kill the hunk at point. | ||
| 2283 | Otherwise kill all hunks overlapped by region delimited by BEG and END. | ||
| 2284 | When killing a hunk that's the only one remaining for its file, kill the | ||
| 2285 | file header too. | ||
| 2286 | If 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. |
| 2287 | Interactively, if the region is active, reverse-apply and kill all | 2359 | Interactively, 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. |