Adapt ediff to nonstandard layouts
Make ediff cope with having some of its windows (especially the control window) not shown by a custom ediff-window-setup-function. Modernize relevant adjacent code. After this change, one can write a custom ediff-window-setup-function that doesn't show the control window. * doc/misc/ediff.texi (Notes on Heavy-duty Customization): Refine language to explain that the window setup function doesn't have to show all windows. * lisp/vc/ediff-util.el (ediff-select-control-window-on-setup): New variable. (ediff-setup, ediff-recenter, ediff-recenter-one-window) (ediff-recenter-ancestor, ediff-toggle-read-only) (ediff-operate-on-windows, ediff-jump-to-difference-at-point) (ediff-default-suspend-function) (ediff-clone-buffer-for-region-comparison) (ediff-clone-buffer-for-window-comparison): Modernize control flow; select only windows that exist. * lisp/vc/ediff-wind.el (ediff-with-live-window): New convenience macro. (ediff-window-setup-function): Explain relaxed contract.
This commit is contained in:
parent
b21636580b
commit
e5ee1d2a74
4 changed files with 124 additions and 141 deletions
|
@ -2399,10 +2399,12 @@ The window displaying buffer A@. If buffer A is not visible, this variable
|
|||
is @code{nil} or it may be a dead window.
|
||||
|
||||
@item ediff-window-B
|
||||
The window displaying buffer B.
|
||||
The window displaying buffer B. If buffer B is not visible, this variable
|
||||
is @code{nil} or it may be a dead window.
|
||||
|
||||
@item ediff-window-C
|
||||
The window displaying buffer C, if any.
|
||||
The window displaying buffer C, if any. If buffer C is not visible,
|
||||
this variable is @code{nil} or it may be a dead window.
|
||||
|
||||
@item ediff-control-frame
|
||||
A dedicated frame displaying the control buffer, if it exists. It is
|
||||
|
|
8
etc/NEWS
8
etc/NEWS
|
@ -1202,6 +1202,14 @@ changes when supplied with a universal prefix argument via 'C-u':
|
|||
- 'C-u c a' copies all changes from buffer C to buffer A.
|
||||
- 'C-u c b' copies all changes from buffer C to buffer B.
|
||||
|
||||
+++
|
||||
*** Ediff now supports more flexible custom window layouts
|
||||
Custom implementations of 'ediff-window-setup-function' no
|
||||
longer need to display *all* ediff windows. Any of the A, B, C,
|
||||
and control windows can be left undisplayed and the corresponding
|
||||
variable set to nil. This change enables custom layouts without
|
||||
a control panel window.
|
||||
|
||||
** Dired
|
||||
|
||||
+++
|
||||
|
|
|
@ -36,6 +36,15 @@
|
|||
(require 'ediff-diff)
|
||||
(require 'ediff-merg)
|
||||
|
||||
(eval-when-compile
|
||||
(require 'cl-lib))
|
||||
|
||||
(ediff-defvar-local ediff-select-control-window-on-setup t
|
||||
"Select the control window after window setup.
|
||||
`t' for compatibility. Custom `ediff-window-setup-function'
|
||||
implementations may want to set it to `nil' to fully control
|
||||
window setup.")
|
||||
|
||||
|
||||
;;; Functions
|
||||
|
||||
|
@ -472,20 +481,20 @@ to invocation.")
|
|||
(ediff-get-value-according-to-buffer-type
|
||||
'C ediff-narrow-bounds))))
|
||||
;; position point in buf A
|
||||
(save-excursion
|
||||
(select-window ediff-window-A)
|
||||
(goto-char shift-A))
|
||||
(when (window-live-p ediff-window-A)
|
||||
(with-selected-window ediff-window-A
|
||||
(goto-char shift-A)))
|
||||
;; position point in buf B
|
||||
(save-excursion
|
||||
(select-window ediff-window-B)
|
||||
(goto-char shift-B))
|
||||
(if ediff-3way-job
|
||||
(save-excursion
|
||||
(select-window ediff-window-C)
|
||||
(goto-char shift-C)))
|
||||
)
|
||||
(when (window-live-p ediff-window-B)
|
||||
(with-selected-window ediff-window-B
|
||||
(goto-char shift-B)))
|
||||
(if (and ediff-3way-job (window-live-p ediff-window-C))
|
||||
(with-selected-window ediff-window-C
|
||||
(goto-char shift-C))))
|
||||
|
||||
(select-window ediff-control-window)
|
||||
(when (and ediff-select-control-window-on-setup
|
||||
(window-live-p ediff-control-window))
|
||||
(select-window ediff-control-window))
|
||||
(ediff-visible-region)
|
||||
|
||||
(mapc #'funcall startup-hooks)
|
||||
|
@ -776,16 +785,19 @@ buffers."
|
|||
(or (not ediff-3way-job)
|
||||
(ediff-buffer-live-p ediff-buffer-C)))
|
||||
(progn
|
||||
(or no-rehighlight
|
||||
(or no-rehighlight
|
||||
(ediff-select-difference ediff-current-difference))
|
||||
|
||||
(ediff-recenter-one-window 'A)
|
||||
(ediff-recenter-one-window 'B)
|
||||
(if ediff-3way-job
|
||||
(ediff-recenter-one-window 'C))
|
||||
(save-current-buffer
|
||||
(ediff-recenter-one-window 'A))
|
||||
(save-current-buffer
|
||||
(ediff-recenter-one-window 'B))
|
||||
(if ediff-3way-job
|
||||
(save-current-buffer
|
||||
(ediff-recenter-one-window 'C)))
|
||||
|
||||
(ediff-with-current-buffer control-buf
|
||||
(ediff-recenter-ancestor) ; check if ancestor is alive
|
||||
(ediff-with-current-buffer control-buf
|
||||
(ediff-recenter-ancestor) ; check if ancestor is alive
|
||||
|
||||
(if (and (ediff-multiframe-setup-p)
|
||||
(not ediff-use-long-help-message)
|
||||
|
@ -801,13 +813,11 @@ buffers."
|
|||
(ediff-with-current-buffer control-buf (ediff-refresh-mode-lines))
|
||||
))
|
||||
|
||||
;; this function returns to the window it was called from
|
||||
;; (which was the control window)
|
||||
;; this function does not change current window
|
||||
(defun ediff-recenter-one-window (buf-type)
|
||||
(if (ediff-valid-difference-p)
|
||||
;; context must be saved before switching to windows A/B/C
|
||||
(let* ((ctl-wind (selected-window))
|
||||
(shift (ediff-overlay-start
|
||||
(let* ((shift (ediff-overlay-start
|
||||
(ediff-get-value-according-to-buffer-type
|
||||
buf-type ediff-narrow-bounds)))
|
||||
(job-name ediff-job-name)
|
||||
|
@ -817,20 +827,16 @@ buffers."
|
|||
(window (if (window-live-p (symbol-value window-name))
|
||||
(symbol-value window-name))))
|
||||
|
||||
(if (and window ediff-windows-job)
|
||||
(when (and window ediff-windows-job)
|
||||
(set-window-start window shift))
|
||||
(if window
|
||||
(progn
|
||||
(select-window window)
|
||||
(deactivate-mark)
|
||||
(ediff-position-region
|
||||
(when window
|
||||
(with-selected-window window
|
||||
(deactivate-mark)
|
||||
(ediff-position-region
|
||||
(ediff-get-diff-posn buf-type 'beg nil control-buf)
|
||||
(ediff-get-diff-posn buf-type 'end nil control-buf)
|
||||
(ediff-get-diff-posn buf-type 'beg nil control-buf)
|
||||
job-name
|
||||
)))
|
||||
(select-window ctl-wind)
|
||||
)))
|
||||
job-name))))))
|
||||
|
||||
(defun ediff-recenter-ancestor ()
|
||||
;; do half-hearted job by recentering the ancestor buffer, if it is alive and
|
||||
|
@ -838,21 +844,17 @@ buffers."
|
|||
(if (and (ediff-buffer-live-p ediff-ancestor-buffer)
|
||||
(ediff-valid-difference-p))
|
||||
(let ((window (ediff-get-visible-buffer-window ediff-ancestor-buffer))
|
||||
(ctl-wind (selected-window))
|
||||
(job-name ediff-job-name)
|
||||
(ctl-buf ediff-control-buffer))
|
||||
(ediff-with-current-buffer ediff-ancestor-buffer
|
||||
(goto-char (ediff-get-diff-posn 'Ancestor 'beg nil ctl-buf))
|
||||
(if window
|
||||
(progn
|
||||
(select-window window)
|
||||
(ediff-position-region
|
||||
(when (window-live-p window)
|
||||
(with-selected-window window
|
||||
(ediff-position-region
|
||||
(ediff-get-diff-posn 'Ancestor 'beg nil ctl-buf)
|
||||
(ediff-get-diff-posn 'Ancestor 'end nil ctl-buf)
|
||||
(ediff-get-diff-posn 'Ancestor 'beg nil ctl-buf)
|
||||
job-name))))
|
||||
(select-window ctl-wind)
|
||||
)))
|
||||
job-name)))))))
|
||||
|
||||
|
||||
;; This will have to be refined for 3way jobs
|
||||
|
@ -1064,10 +1066,9 @@ of the current buffer."
|
|||
(sit-for 3)))) ; let the user see the warning
|
||||
(if (and toggle-ro-cmd
|
||||
(string-match "read-only-mode" (symbol-name toggle-ro-cmd)))
|
||||
(save-excursion
|
||||
(save-window-excursion
|
||||
(select-window (ediff-get-visible-buffer-window buf))
|
||||
(command-execute toggle-ro-cmd)))
|
||||
(save-window-excursion
|
||||
(ediff-with-live-window (ediff-get-visible-buffer-window buf)
|
||||
(command-execute toggle-ro-cmd)))
|
||||
(user-error "Don't know how to toggle read-only in buffer %S" buf))
|
||||
|
||||
;; Check if we made the current buffer updatable, but its file is RO.
|
||||
|
@ -1413,8 +1414,8 @@ Used in ediff-windows/regions only."
|
|||
(defun ediff-operate-on-windows (operation arg)
|
||||
|
||||
;; make sure windows aren't dead
|
||||
(if (not (and (window-live-p ediff-window-A) (window-live-p ediff-window-B)))
|
||||
(ediff-recenter 'no-rehighlight))
|
||||
(unless (and (window-live-p ediff-window-A) (window-live-p ediff-window-B))
|
||||
(ediff-recenter 'no-rehighlight))
|
||||
(if (not (and (ediff-buffer-live-p ediff-buffer-A)
|
||||
(ediff-buffer-live-p ediff-buffer-B)
|
||||
(or (not ediff-3way-job) (ediff-buffer-live-p ediff-buffer-C))
|
||||
|
@ -1424,8 +1425,7 @@ Used in ediff-windows/regions only."
|
|||
))
|
||||
(error ediff-KILLED-VITAL-BUFFER))
|
||||
|
||||
(let* ((wind (selected-window))
|
||||
(wind-A ediff-window-A)
|
||||
(let* ((wind-A ediff-window-A)
|
||||
(wind-B ediff-window-B)
|
||||
(wind-C ediff-window-C)
|
||||
(wind-Anc ediff-window-Ancestor)
|
||||
|
@ -1438,26 +1438,16 @@ Used in ediff-windows/regions only."
|
|||
(coefAnc (if with-Ancestor
|
||||
(ediff-get-region-size-coefficient 'Ancestor operation))))
|
||||
|
||||
(select-window wind-A)
|
||||
(condition-case nil
|
||||
(funcall operation (round (* coefA arg)))
|
||||
(error))
|
||||
(select-window wind-B)
|
||||
(condition-case nil
|
||||
(funcall operation (round (* coefB arg)))
|
||||
(error))
|
||||
(if three-way
|
||||
(progn
|
||||
(select-window wind-C)
|
||||
(condition-case nil
|
||||
(funcall operation (round (* coefC arg)))
|
||||
(error))))
|
||||
(ediff-with-live-window wind-A
|
||||
(ignore-errors (funcall operation (round (* coefA arg)))))
|
||||
(ediff-with-live-window wind-B
|
||||
(ignore-errors (funcall operation (round (* coefB arg)))))
|
||||
(when three-way
|
||||
(ediff-with-live-window wind-C
|
||||
(ignore-errors (funcall operation (round (* coefC arg))))))
|
||||
(when with-Ancestor
|
||||
(select-window wind-Anc)
|
||||
(condition-case nil
|
||||
(funcall operation (round (* coefAnc arg)))
|
||||
(error)))
|
||||
(select-window wind)))
|
||||
(ediff-with-live-window wind-Anc
|
||||
(ignore-errors (funcall operation (round (* coefAnc arg))))))))
|
||||
|
||||
(defun ediff-scroll-vertically (&optional arg)
|
||||
"Vertically scroll buffers A, B (and C if appropriate).
|
||||
|
@ -1817,44 +1807,29 @@ current point position in the specified buffer."
|
|||
(beg (if past-last-diff
|
||||
(ediff-with-current-buffer buffer (point-max))
|
||||
(ediff-get-diff-posn buf-type 'beg (1- diff-no))))
|
||||
ctl-wind wind-A wind-B wind-C
|
||||
wind-A wind-B wind-C
|
||||
shift)
|
||||
(if past-last-diff
|
||||
(ediff-jump-to-difference -1)
|
||||
(ediff-jump-to-difference diff-no))
|
||||
(setq ctl-wind (selected-window)
|
||||
wind-A ediff-window-A
|
||||
(setq wind-A ediff-window-A
|
||||
wind-B ediff-window-B
|
||||
wind-C ediff-window-C)
|
||||
(if arg
|
||||
(progn
|
||||
(ediff-with-current-buffer buffer
|
||||
(setq shift (- beg pt)))
|
||||
(select-window wind-A)
|
||||
(if past-last-diff (goto-char (point-max)))
|
||||
(condition-case nil
|
||||
(backward-char shift) ; noerror, if beginning of buffer
|
||||
(error))
|
||||
(recenter)
|
||||
(select-window wind-B)
|
||||
(if past-last-diff (goto-char (point-max)))
|
||||
(condition-case nil
|
||||
(backward-char shift) ; noerror, if beginning of buffer
|
||||
(error))
|
||||
(recenter)
|
||||
(if (window-live-p wind-C)
|
||||
(progn
|
||||
(select-window wind-C)
|
||||
(if past-last-diff (goto-char (point-max)))
|
||||
(condition-case nil
|
||||
(backward-char shift) ; noerror, if beginning of buffer
|
||||
(error))
|
||||
(recenter)
|
||||
))
|
||||
(select-window ctl-wind)
|
||||
))
|
||||
))
|
||||
|
||||
(save-selected-window
|
||||
(setq shift (- beg pt))
|
||||
(ediff-with-live-window wind-A
|
||||
(when past-last-diff (goto-char (point-max)))
|
||||
(ignore-errors (backward-char shift))
|
||||
(recenter))
|
||||
(ediff-with-live-window wind-B
|
||||
(when past-last-diff (goto-char (point-max)))
|
||||
(ignore-errors (backward-char shift))
|
||||
(recenter))
|
||||
(ediff-with-live-window wind-C
|
||||
(when past-last-diff (goto-char (point-max)))
|
||||
(ignore-errors (backward-char shift))
|
||||
(recenter))))))
|
||||
|
||||
;; find region most related to the current point position (or POS, if given)
|
||||
;; returns diff number as seen by the user (i.e., 1+ the internal
|
||||
|
@ -2725,10 +2700,7 @@ only if this merge job is part of a group, i.e., was invoked from within
|
|||
(let* ((buf-A ediff-buffer-A)
|
||||
(buf-B ediff-buffer-B)
|
||||
(buf-C ediff-buffer-C)
|
||||
(buf-A-wind (ediff-get-visible-buffer-window buf-A))
|
||||
(buf-B-wind (ediff-get-visible-buffer-window buf-B))
|
||||
(buf-C-wind (ediff-get-visible-buffer-window buf-C))
|
||||
(buf-patch (if (boundp 'ediff-patchbufer) ediff-patchbufer nil))
|
||||
(buf-patch (if (boundp 'ediff-patchbufer) ediff-patchbufer nil))
|
||||
(buf-patch-diag (if (boundp 'ediff-patch-diagnostics)
|
||||
ediff-patch-diagnostics nil))
|
||||
(buf-err ediff-error-buffer)
|
||||
|
@ -2746,35 +2718,18 @@ only if this merge job is part of a group, i.e., was invoked from within
|
|||
(if buf-fine-diff (bury-buffer buf-fine-diff))
|
||||
(if buf-patch (bury-buffer buf-patch))
|
||||
(if buf-patch-diag (bury-buffer buf-patch-diag))
|
||||
(if (window-live-p buf-A-wind)
|
||||
(progn
|
||||
(select-window buf-A-wind)
|
||||
(delete-other-windows)
|
||||
(bury-buffer))
|
||||
(if (ediff-buffer-live-p buf-A)
|
||||
(progn
|
||||
(set-buffer buf-A)
|
||||
(bury-buffer))))
|
||||
(if (window-live-p buf-B-wind)
|
||||
(progn
|
||||
(select-window buf-B-wind)
|
||||
(delete-other-windows)
|
||||
(bury-buffer))
|
||||
(if (ediff-buffer-live-p buf-B)
|
||||
(progn
|
||||
(set-buffer buf-B)
|
||||
(bury-buffer))))
|
||||
(if (window-live-p buf-C-wind)
|
||||
(progn
|
||||
(select-window buf-C-wind)
|
||||
(delete-other-windows)
|
||||
(bury-buffer))
|
||||
(if (ediff-buffer-live-p buf-C)
|
||||
(progn
|
||||
(set-buffer buf-C)
|
||||
(bury-buffer))))
|
||||
))
|
||||
|
||||
(cl-loop
|
||||
with buffers = (list buf-A buf-B buf-C)
|
||||
with windows = (mapcar #'ediff-get-visible-buffer-window buffers)
|
||||
for buffer in buffers
|
||||
for window in windows
|
||||
do (cond ((window-live-p window)
|
||||
(select-window window)
|
||||
(delete-other-windows)
|
||||
(bury-buffer))
|
||||
(buffer
|
||||
(set-buffer buffer)
|
||||
(bury-buffer))))))
|
||||
|
||||
(defun ediff-suspend ()
|
||||
"Suspend Ediff.
|
||||
|
@ -3274,8 +3229,9 @@ Without an argument, it saves customized diff argument, if available
|
|||
(setq ediff-temp-indirect-buffer t))
|
||||
(pop-to-buffer cloned-buff)
|
||||
(setq wind (ediff-get-visible-buffer-window cloned-buff))
|
||||
(select-window wind)
|
||||
(delete-other-windows)
|
||||
(when (window-live-p wind)
|
||||
(select-window wind)
|
||||
(delete-other-windows))
|
||||
(or (mark) (push-mark))
|
||||
(setq mark-active 'ediff-util)
|
||||
(setq-local transient-mark-mode t)
|
||||
|
@ -3310,7 +3266,8 @@ Without an argument, it saves customized diff argument, if available
|
|||
(let ((cloned-buff (ediff-make-cloned-buffer buff region-name)))
|
||||
(ediff-with-current-buffer cloned-buff
|
||||
(setq ediff-temp-indirect-buffer t))
|
||||
(set-window-buffer wind cloned-buff)
|
||||
(when (window-live-p wind)
|
||||
(set-window-buffer wind cloned-buff))
|
||||
cloned-buff))
|
||||
|
||||
(defun ediff-buffer-type (buffer)
|
||||
|
|
|
@ -53,16 +53,21 @@ frame.
|
|||
|
||||
If you don't like any of the two provided functions, write your own one.
|
||||
The basic guidelines:
|
||||
1. It should leave the control buffer current and the control window
|
||||
selected.
|
||||
1. It should leave the control buffer current and, if showing,
|
||||
the control window selected if showing these windows.
|
||||
2. It should set `ediff-window-A', `ediff-window-B', `ediff-window-C',
|
||||
and `ediff-control-window' to contain window objects that display
|
||||
the corresponding buffers.
|
||||
the corresponding buffers or `nil' if the corresponding window
|
||||
is not shown.
|
||||
3. It should accept the following arguments:
|
||||
buffer-A, buffer-B, buffer-C, control-buffer
|
||||
Buffer C may not be used in jobs that compare only two buffers.
|
||||
If you plan to do something fancy, take a close look at how the two
|
||||
provided functions are written."
|
||||
provided functions are written.
|
||||
|
||||
Set `ediff-select-control-window-on-setup' to nil to prevent the window
|
||||
`ediff-control-window' being selected by ediff after this
|
||||
function returns. "
|
||||
:type '(choice (const :tag "Choose Automatically" ediff-setup-windows-default)
|
||||
(const :tag "Multi Frame" ediff-setup-windows-multiframe)
|
||||
(const :tag "Single Frame" ediff-setup-windows-plain)
|
||||
|
@ -247,6 +252,17 @@ keyboard input to go into icons."
|
|||
|
||||
;;; Functions
|
||||
|
||||
(defmacro ediff-with-live-window (window &rest body)
|
||||
"Like `with-selected-window' but only if WINDOW is live.
|
||||
If WINDOW is not live (or not a window) do nothing and don't evaluate
|
||||
BODY, instead returning nil."
|
||||
(declare (indent 1) (debug (form body)))
|
||||
(let ((w (gensym "window")))
|
||||
`(let ((,w ,window))
|
||||
(when (window-live-p ,w)
|
||||
(with-selected-window ,w
|
||||
,@body)))))
|
||||
|
||||
(defun ediff-get-window-by-clicking (_wind _prev-wind wind-number)
|
||||
(let (event)
|
||||
(message
|
||||
|
|
Loading…
Add table
Reference in a new issue