mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-02-16 09:14:18 +00:00
Make diff-hunk-kill respect an active region
* lisp/vc/diff-mode.el (diff--revert-kill-hunks): New workhorse routine. (diff-hunk-kill, diff-revert-and-kill-hunk): Call it. (diff-hunk-kill): New BEG and END parameters and interactive form. * doc/emacs/files.texi (Diff Mode): * etc/NEWS: Document the change.
This commit is contained in:
parent
ae7761598d
commit
fcdd8678f9
4 changed files with 98 additions and 51 deletions
|
|
@ -1835,7 +1835,8 @@ the start of the @var{n}th previous file.
|
|||
|
||||
@findex diff-hunk-kill
|
||||
@item M-k
|
||||
Kill the hunk at point (@code{diff-hunk-kill}).
|
||||
Kill the hunk at point (@code{diff-hunk-kill}). If the region is
|
||||
active, kills all hunks the region overlaps.
|
||||
|
||||
@findex diff-file-kill
|
||||
@item M-K
|
||||
|
|
|
|||
7
etc/NEWS
7
etc/NEWS
|
|
@ -2284,9 +2284,10 @@ one as before. This makes them different from 'vc-diff' and
|
|||
*** 'diff-apply-hunk' now supports creating and deleting files.
|
||||
|
||||
+++
|
||||
*** 'diff-apply-hunk' and 'diff-apply-buffer' now consider the region.
|
||||
If the region is active, these commands now apply all hunks that the
|
||||
region overlaps. Otherwise, they have their existing behavior.
|
||||
*** Diff mode's application and killing commands now consider the region.
|
||||
If the region is active, 'diff-apply-hunk', 'diff-apply-buffer' and
|
||||
'diff-hunk-kill' now apply or kill all hunks that the region overlaps.
|
||||
Otherwise, they have their existing behavior.
|
||||
|
||||
+++
|
||||
*** 'diff-apply-buffer' can reverse-apply.
|
||||
|
|
|
|||
|
|
@ -883,31 +883,19 @@ If the prefix ARG is given, restrict the view to the current file instead."
|
|||
(goto-char (point-min))
|
||||
(re-search-forward diff-hunk-header-re nil t)))
|
||||
|
||||
(defun diff-hunk-kill ()
|
||||
"Kill the hunk at point."
|
||||
(interactive)
|
||||
(if (not (diff--some-hunks-p))
|
||||
(error "No hunks")
|
||||
(diff-beginning-of-hunk t)
|
||||
(let* ((hunk-bounds (diff-bounds-of-hunk))
|
||||
(file-bounds (ignore-errors (diff-bounds-of-file)))
|
||||
;; If the current hunk is the only one for its file, kill the
|
||||
;; file header too.
|
||||
(bounds (if (and file-bounds
|
||||
(progn (goto-char (car file-bounds))
|
||||
(= (progn (diff-hunk-next) (point))
|
||||
(car hunk-bounds)))
|
||||
(progn (goto-char (cadr hunk-bounds))
|
||||
;; bzr puts a newline after the last hunk.
|
||||
(while (looking-at "^\n")
|
||||
(forward-char 1))
|
||||
(= (point) (cadr file-bounds))))
|
||||
file-bounds
|
||||
hunk-bounds))
|
||||
(inhibit-read-only t))
|
||||
(apply #'kill-region bounds)
|
||||
(goto-char (car bounds))
|
||||
(ignore-errors (diff-beginning-of-hunk t)))))
|
||||
(defun diff-hunk-kill (&optional beg end)
|
||||
"Kill the hunk at point.
|
||||
When killing the last hunk left for a file, kill the file header too.
|
||||
Interactively, if the region is active, kill all hunks that the region
|
||||
overlaps.
|
||||
|
||||
When called from Lisp with optional arguments BEG and END non-nil, kill
|
||||
all hunks overlapped by the region from BEG to END as though called
|
||||
interactively with an active region delimited by BEG and END."
|
||||
(interactive "R")
|
||||
(when (xor beg end)
|
||||
(error "Invalid call to `diff-hunk-kill'"))
|
||||
(diff--revert-kill-hunks beg end nil))
|
||||
|
||||
;; This is not `diff-kill-other-hunks' because we might need to make
|
||||
;; copies of file headers in order to ensure the new kill ring entry
|
||||
|
|
@ -2283,6 +2271,83 @@ With a prefix argument, try to REVERSE the hunk."
|
|||
:type 'boolean
|
||||
:version "31.1")
|
||||
|
||||
(defun diff--revert-kill-hunks (beg end revertp)
|
||||
"Workhorse routine for killing hunks, after possibly reverting them.
|
||||
If BEG and END are nil, kill the hunk at point.
|
||||
Otherwise kill all hunks overlapped by region delimited by BEG and END.
|
||||
When killing a hunk that's the only one remaining for its file, kill the
|
||||
file header too.
|
||||
If REVERTP is non-nil, reverse-apply hunks before killing them."
|
||||
;; With BEG and END non-nil, we push each hunk to the kill ring
|
||||
;; separately. If we want to push to the kill ring just once, we have
|
||||
;; to decide how to handle file headers such that the meanings of the
|
||||
;; hunks in the kill ring entry, considered as a whole patch, do not
|
||||
;; deviate too far from the meanings the hunks had in this buffer.
|
||||
;;
|
||||
;; For example, if we have a single hunk for one file followed by
|
||||
;; multiple hunks for another file, and we naïvely kill the single
|
||||
;; hunk and the first of the multiple hunks, our kill ring entry will
|
||||
;; be a patch applying those two hunks to the first file. This is
|
||||
;; because killing the single hunk will have brought its file header
|
||||
;; with it, but not so killing the second hunk. So we will have put
|
||||
;; together hunks that were previously for two different files.
|
||||
;;
|
||||
;; One option is to *copy* every file header that the region overlaps
|
||||
;; (and that we will not kill, because we are leaving other hunks for
|
||||
;; that file behind). But then the text this command pushes to the
|
||||
;; kill ring would be different from the text it removes from the
|
||||
;; buffer, which would be unintuitive for an Emacs kill command.
|
||||
;;
|
||||
;; An alternative might be to have restrictions as follows:
|
||||
;;
|
||||
;; Interactively, if the region is active, try to kill all hunks that the
|
||||
;; region overlaps. This works when either
|
||||
;; - all the hunks the region overlaps are for the same file; or
|
||||
;; - the last hunk the region overlaps is the last hunk for its file.
|
||||
;; These restrictions are so that the text added to the kill ring does not
|
||||
;; merge together hunks for different files under a single file header.
|
||||
;;
|
||||
;; We would error out if neither property is met. When either holds,
|
||||
;; any file headers the region overlaps are ones we should kill.
|
||||
(unless (diff--some-hunks-p)
|
||||
(error "No hunks"))
|
||||
(if beg
|
||||
(save-excursion
|
||||
(goto-char beg)
|
||||
(setq beg (car (diff-bounds-of-hunk)))
|
||||
(goto-char end)
|
||||
(unless (looking-at diff-hunk-header-re)
|
||||
(setq end (cadr (diff-bounds-of-hunk)))))
|
||||
(pcase-setq `(,beg ,end) (diff-bounds-of-hunk)))
|
||||
(when (or (not revertp) (null (diff-apply-buffer beg end t)))
|
||||
(goto-char end)
|
||||
(when-let* ((pos (diff--at-diff-header-p)))
|
||||
(goto-char pos))
|
||||
(setq beg (copy-marker beg) end (point-marker))
|
||||
(unwind-protect
|
||||
(cl-loop initially (goto-char beg)
|
||||
for (hunk-beg hunk-end) = (diff-bounds-of-hunk)
|
||||
for file-bounds = (ignore-errors (diff-bounds-of-file))
|
||||
for (file-beg file-end) = file-bounds
|
||||
for inhibit-read-only = t
|
||||
if (and file-bounds
|
||||
(progn
|
||||
(goto-char file-beg)
|
||||
(diff-hunk-next)
|
||||
(eq (point) hunk-beg))
|
||||
(progn
|
||||
(goto-char hunk-end)
|
||||
;; bzr puts a newline after the last hunk.
|
||||
(while (looking-at "^\n") (forward-char 1))
|
||||
(eq (point) file-end)))
|
||||
do (kill-region file-beg file-end) (goto-char file-beg)
|
||||
else do (kill-region hunk-beg hunk-end) (goto-char hunk-beg)
|
||||
do (ignore-errors (diff-beginning-of-hunk t))
|
||||
until (or (< (point) (marker-position beg))
|
||||
(eql (point) (marker-position end))))
|
||||
(set-marker beg nil)
|
||||
(set-marker end nil))))
|
||||
|
||||
(defun diff-revert-and-kill-hunk (&optional beg end)
|
||||
"Reverse-apply and then kill the hunk at point. Save changed buffer.
|
||||
Interactively, if the region is active, reverse-apply and kill all
|
||||
|
|
@ -2308,27 +2373,7 @@ BEG and END."
|
|||
(error "Invalid call to `diff-revert-and-kill-hunk'"))
|
||||
(when (or (not diff-ask-before-revert-and-kill-hunk)
|
||||
(y-or-n-p "Really reverse-apply and kill hunk(s)?"))
|
||||
(if beg
|
||||
(save-excursion
|
||||
(goto-char beg)
|
||||
(setq beg (car (diff-bounds-of-hunk)))
|
||||
(goto-char end)
|
||||
(unless (looking-at diff-hunk-header-re)
|
||||
(setq end (cadr (diff-bounds-of-hunk)))))
|
||||
(pcase-setq `(,beg ,end) (diff-bounds-of-hunk)))
|
||||
(when (null (diff-apply-buffer beg end t))
|
||||
;; Use `diff-hunk-kill' because it properly handles file headers.
|
||||
(goto-char end)
|
||||
(when-let* ((pos (diff--at-diff-header-p)))
|
||||
(goto-char pos))
|
||||
(setq beg (copy-marker beg) end (point-marker))
|
||||
(unwind-protect
|
||||
(cl-loop initially (goto-char beg)
|
||||
do (diff-hunk-kill)
|
||||
until (or (< (point) (marker-position beg))
|
||||
(eql (point) (marker-position end))))
|
||||
(set-marker beg nil)
|
||||
(set-marker end nil)))))
|
||||
(diff--revert-kill-hunks beg end t)))
|
||||
|
||||
(defun diff-apply-buffer (&optional beg end reverse test-or-no-save)
|
||||
"Apply the diff in the entire diff buffer.
|
||||
|
|
|
|||
|
|
@ -811,7 +811,7 @@ This works by considering the current branch as a topic branch
|
|||
(whether or not it actually is).
|
||||
|
||||
If there is a distinct push remote for this branch, assume the target
|
||||
for outstanding changes is the tracking branch, so return that.
|
||||
for outstanding changes is the tracking branch, and return that.
|
||||
|
||||
Otherwise, fall back to the following algorithm, which requires that the
|
||||
corresponding trunk exists as a local branch. Find all merge bases
|
||||
|
|
|
|||
Loading…
Reference in a new issue