branch: elpa/magit
commit ad79d46df92b38c2f43d48f6c6f49781186bdf72
Author: Jonas Bernoulli <[email protected]>
Commit: Jonas Bernoulli <[email protected]>
magit-ediff-buffers: Replace with simpler implementation
Take advantage of `magit-find-file-noselect's new VOLATILE argument.
No longer call `magit-get-revision-buffer' to determine whether the
buffer already exists; go straight to `magit-find-file-noselect'.
If the buffer did not exist already exist, `magit-buffer--volatile'
is set to `ediff' and when we quit and encounter that value, we kill
the buffer. Otherwise we don't.
Hard-code the cleanup function.
We can now delete `magit-get-revision-buffer'.
---
lisp/magit-ediff.el | 221 ++++++++++++++++++++--------------------------------
1 file changed, 83 insertions(+), 138 deletions(-)
diff --git a/lisp/magit-ediff.el b/lisp/magit-ediff.el
index b4d4bd41ca..1d4041fcf5 100644
--- a/lisp/magit-ediff.el
+++ b/lisp/magit-ediff.el
@@ -42,19 +42,17 @@
:group 'magit-extensions)
(defcustom magit-ediff-quit-hook
- (list #'magit-ediff-cleanup-auxiliary-buffers
- #'magit-ediff-restore-previous-winconf)
+ (list #'magit-ediff-restore-previous-winconf)
"Hooks to run after finishing Ediff, when that was invoked using Magit.
The hooks are run in the Ediff control buffer. This is similar
to `ediff-quit-hook' but takes the needs of Magit into account.
The `ediff-quit-hook' is ignored by Ediff sessions which were
invoked using Magit."
- :package-version '(magit . "2.2.0")
+ :package-version '(magit . "4.6.0")
:group 'magit-ediff
:type 'hook
:get #'magit-hook-custom-get
- :options (list #'magit-ediff-cleanup-auxiliary-buffers
- #'magit-ediff-restore-previous-winconf))
+ :options (list #'magit-ediff-restore-previous-winconf))
(defcustom magit-ediff-dwim-resolve-function #'magit-ediff-resolve-rest
"The function `magit-ediff-dwim' uses to resolve conflicts."
@@ -132,84 +130,6 @@ recommend you do not further complicate that by enabling
this.")
("r" "Show range" magit-ediff-compare)
("z" "Show stash" magit-ediff-show-stash)]])
-(defmacro magit-ediff-buffers (a b &optional c setup quit file)
- "Run Ediff on two or three buffers.
-This is a wrapper around `ediff-buffers-internal'.
-
-A, B and C have the form (GET-BUFFER CREATE-BUFFER). If
-GET-BUFFER returns a non-nil value, then that buffer is used and
-it is not killed when exiting Ediff. Otherwise CREATE-BUFFER
-must return a buffer and that is killed when exiting Ediff.
-
-If non-nil, SETUP must be a function. It is called without
-arguments after Ediff is done setting up buffers.
-
-If non-nil, QUIT must be a function. It is added to
-`ediff-quit-hook' and is called without arguments.
-
-If FILE is non-nil, then perform a merge. The merge result
-is put in FILE."
- (let (get make kill (char ?A))
- (dolist (spec (list a b c))
- (if (not spec)
- (push nil make)
- (pcase-let ((`(,g ,m) spec))
- (let ((b (intern (format "buf%c" char))))
- (push `(,b ,g) get)
- ;; This is an unfortunate complication that I have added for
- ;; the benefit of one user. Pretend we used this instead:
- ;; (push `(or ,b ,m) make)
- (push `(if ,b
- (if magit-ediff-use-indirect-buffers
- (prog1 (make-indirect-buffer
- ,b
- (generate-new-buffer-name (buffer-name ,b))
- t)
- (setq ,b nil))
- ,b)
- ,m)
- make)
- (push `(unless ,b
- ;; For merge jobs Ediff switches buffer names around.
- ;; See (if ediff-merge-job ...) in `ediff-setup'.
- (let ((var ,(if (and file (= char ?C))
- 'ediff-ancestor-buffer
- (intern (format "ediff-buffer-%c" char)))))
- (ediff-kill-buffer-carefully var)))
- kill))
- (cl-incf char))))
- (setq get (nreverse get))
- (setq make (nreverse make))
- (setq kill (nreverse kill))
- (let ((mconf (gensym "conf"))
- (mfile (gensym "file")))
- `(magit-with-toplevel
- (let ((,mconf (current-window-configuration))
- (,mfile ,file)
- ,@get)
- (ediff-buffers-internal
- ,@make
- (list ,@(and setup (list setup))
- (lambda ()
- ;; We do not want to kill buffers that existed before
- ;; Ediff was invoked, so we cannot use Ediff's default
- ;; quit functions. Ediff splits quitting across two
- ;; hooks for merge jobs but we only ever use one.
- (setq-local ediff-quit-merge-hook nil)
- (setq-local ediff-quit-hook
- (list
- ,@(and quit (list quit))
- (lambda ()
- ,@kill
- (let ((magit-ediff-previous-winconf ,mconf))
- (run-hooks 'magit-ediff-quit-hook)))))))
- (pcase (list ,(and c t) (and ,mfile t))
- ('(nil nil) 'ediff-buffers)
- ('(nil t) 'ediff-merge-buffers)
- ('(t nil) 'ediff-buffers3)
- ('(t t) 'ediff-merge-buffers-with-ancestor))
- ,mfile))))))
-
;;;###autoload
(defun magit-ediff-resolve-all (file)
"Resolve all conflicts in the FILE at point using Ediff.
@@ -266,21 +186,11 @@ and alternative commands."
(goto-char (point-min))
(unless (re-search-forward "^<<<<<<< " nil t)
(magit-stage-files (list file)))))))))
- (cond (fileC
- (magit-ediff-buffers
- ((magit-get-revision-buffer revA fileA)
- (magit-ediff--find-file revA fileA))
- ((magit-get-revision-buffer revB fileB)
- (magit-ediff--find-file revB fileB))
- ((magit-get-revision-buffer revC fileC)
- (magit-ediff--find-file revC fileC))
- setup quit file))
- ((magit-ediff-buffers
- ((magit-get-revision-buffer revA fileA)
- (magit-ediff--find-file revA fileA))
- ((magit-get-revision-buffer revB fileB)
- (magit-ediff--find-file revB fileB))
- nil setup quit file)))))))
+ (magit-ediff-buffers
+ (magit-ediff--find-file revA fileA)
+ (magit-ediff--find-file revB fileB)
+ (and fileC (magit-ediff--find-file revC fileC))
+ setup quit file)))))
;;;###autoload
(defun magit-ediff-resolve-rest (file)
@@ -323,23 +233,17 @@ FILE has to be relative to the top directory of the
repository."
(list (magit-completing-read "Selectively stage file" files nil t nil nil
(car (member (magit-current-file)
files))))))
(magit-with-toplevel
- (let* ((bufA (magit-get-revision-buffer "HEAD" file))
- (bufB (magit-get-revision-buffer "{index}" file))
- (lockB (and bufB (buffer-local-value 'buffer-read-only bufB)))
- (bufC (get-file-buffer file))
+ (let* ((bufC (magit-ediff--find-file "{worktree}" file))
;; Use the same encoding for all three buffers or we
;; may end up changing the file in an unintended way.
- (bufC* (or bufC (find-file-noselect file)))
(coding-system-for-read
- (buffer-local-value 'buffer-file-coding-system bufC*))
- (bufA* (magit-ediff--find-file "HEAD" file))
- (bufB* (magit-ediff--find-file "{index}" file)))
- (with-current-buffer bufB* (setq buffer-read-only nil))
+ (buffer-local-value 'buffer-file-coding-system bufC))
+ (bufA (magit-ediff--find-file "HEAD" file))
+ (bufB (magit-ediff--find-file "{index}" file))
+ (lockB (buffer-local-value 'buffer-read-only bufB)))
+ (with-current-buffer bufB (setq buffer-read-only nil))
(magit-ediff-buffers
- (bufA bufA*)
- (bufB bufB*)
- (bufC bufC*)
- nil
+ bufA bufB bufC nil
(lambda ()
(when (buffer-live-p ediff-buffer-B)
(when lockB
@@ -372,10 +276,8 @@ range)."
(nconc (list revA revB)
(magit-ediff-read-files revA revB))))
(magit-ediff-buffers
- ((magit-get-revision-buffer (or revA "{worktree}") fileA)
- (magit-ediff--find-file (or revA "{worktree}") fileA))
- ((magit-get-revision-buffer (or revB "{worktree}") fileB)
- (magit-ediff--find-file (or revB "{worktree}") fileB))))
+ (magit-ediff--find-file (or revA "{worktree}") fileA)
+ (magit-ediff--find-file (or revB "{worktree}") fileB)))
(defun magit-ediff-compare--read-revisions (&optional arg mbase)
(let ((input (or arg (magit-diff-read-range-or-commit
@@ -502,10 +404,8 @@ FILE must be relative to the top directory of the
repository."
(list (magit-read-file-choice "Show staged changes for file"
(magit-staged-files)
"No staged files")))
- (magit-ediff-buffers ((magit-get-revision-buffer "HEAD" file)
- (magit-ediff--find-file "HEAD" file))
- ((magit-get-revision-buffer "{index}" file)
- (magit-ediff--find-file "{index}" file))))
+ (magit-ediff-buffers (magit-ediff--find-file "HEAD" file)
+ (magit-ediff--find-file "{index}" file)))
;;;###autoload
(defun magit-ediff-show-unstaged (file)
@@ -519,10 +419,8 @@ FILE must be relative to the top directory of the
repository."
(list (magit-read-file-choice "Show unstaged changes for file"
(magit-unstaged-files)
"No unstaged files")))
- (magit-ediff-buffers ((magit-get-revision-buffer "{index}" file)
- (magit-ediff--find-file "{index}" file))
- ((magit-get-revision-buffer "{worktree}" file)
- (magit-ediff--find-file "{worktree}" file))))
+ (magit-ediff-buffers (magit-ediff--find-file "{index}" file)
+ (magit-ediff--find-file "{worktree}" file)))
;;;###autoload
(defun magit-ediff-show-working-tree (file)
@@ -532,10 +430,8 @@ FILE must be relative to the top directory of the
repository."
(list (magit-read-file-choice "Show changes in file"
(magit-changed-files "HEAD")
"No changed files")))
- (magit-ediff-buffers ((magit-get-revision-buffer "HEAD" file)
- (magit-ediff--find-file "HEAD" file))
- ((magit-get-revision-buffer "{worktree}" file)
- (magit-ediff--find-file "{worktree}" file))))
+ (magit-ediff-buffers (magit-ediff--find-file "HEAD" file)
+ (magit-ediff--find-file "{worktree}" file)))
;;;###autoload
(defun magit-ediff-show-commit (commit)
@@ -562,21 +458,65 @@ stash that were staged."
(if (and magit-ediff-show-stash-with-index
(member fileA (magit-changed-files revB revA)))
(magit-ediff-buffers
- ((magit-get-revision-buffer revA fileA)
- (magit-ediff--find-file revA fileA))
- ((magit-get-revision-buffer revB fileB)
- (magit-ediff--find-file revB fileB))
- ((magit-get-revision-buffer revC fileC)
- (magit-ediff--find-file revC fileC)))
+ (magit-ediff--find-file revA fileA)
+ (magit-ediff--find-file revB fileB)
+ (magit-ediff--find-file revC fileC))
(magit-ediff-compare revA revC fileA fileC))))
-(defun magit-get-revision-buffer (rev file)
- (get-buffer (magit--blob-buffer-name rev file)))
+;;; Setup
-(defun magit-ediff--find-file (rev file)
- (magit-find-file-noselect rev file t))
+(defun magit-ediff-buffers (a b &optional c setup quit file)
+ "Run Ediff on two or three buffers A, B and C.
-(defun magit-ediff-cleanup-auxiliary-buffers ()
+If optional FILE is non-nil, then perform a merge. The merge result
+is put in FILE.
+
+Neutralize the hooks `ediff-quit-hook' and `ediff-quit-merge-hook'
+because they usually feature functions that do not work for Magit.
+Instead run optional QUIT (if non-nil), `magit-ediff--cleanup-buffers'
+and `magit-ediff-quit-hook', with no arguments.
+
+Optional SETUP, if non-nil, is called with no arguments after Ediff
+is done setting up buffers."
+ (magit-with-toplevel
+ (ediff-buffers-internal
+ a b c
+ (let ((winconf (current-window-configuration)))
+ (list (lambda ()
+ (when setup
+ (funcall setup))
+ (setq-local ediff-quit-merge-hook nil)
+ (setq-local ediff-quit-hook nil)
+ (when quit
+ (add-hook 'ediff-quit-hook quit nil t))
+ (add-hook 'ediff-quit-hook #'magit-ediff--cleanup-buffers t t)
+ (add-hook 'ediff-quit-hook
+ (lambda ()
+ (let ((magit-ediff-previous-winconf winconf))
+ (run-hooks 'magit-ediff-quit-hook)))
+ t t))))
+ (pcase (list (and c t) (and file t))
+ ('(nil nil) 'ediff-buffers)
+ ('(nil t) 'ediff-merge-buffers)
+ ('(t nil) 'ediff-buffers3)
+ ('(t t) 'ediff-merge-buffers-with-ancestor))
+ file)))
+
+(defun magit-ediff--find-file (rev file)
+ (let ((buffer (magit-find-file-noselect rev file t 'ediff)))
+ (when magit-ediff-use-indirect-buffers
+ (setq buffer (make-indirect-buffer
+ buffer (generate-new-buffer-name (buffer-name buffer)) t)))
+ (with-current-buffer buffer (font-lock-ensure))
+ buffer))
+
+;;; Quit
+
+(defun magit-ediff--cleanup-buffers ()
+ (magit-ediff--bury-buffer ediff-buffer-A)
+ (magit-ediff--bury-buffer ediff-buffer-B)
+ (magit-ediff--bury-buffer ediff-buffer-C)
+ (magit-ediff--bury-buffer ediff-ancestor-buffer)
(let* ((ctl-buf ediff-control-buffer)
(ctl-win (ediff-get-visible-buffer-window ctl-buf))
(ctl-frm ediff-control-frame)
@@ -602,6 +542,11 @@ stash that were staged."
(when (frame-live-p main-frame)
(select-frame main-frame))))
+(defun magit-ediff--bury-buffer (buffer)
+ (when (and (ediff-buffer-live-p buffer)
+ (eq (buffer-local-value 'magit-buffer--volatile buffer) 'ediff))
+ (kill-buffer (get-buffer buffer))))
+
(defun magit-ediff-restore-previous-winconf ()
(set-window-configuration magit-ediff-previous-winconf))