Function: eca-diff-show-ediff

eca-diff-show-ediff is a natively compiled function defined in eca-diff.el.

Signature

(eca-diff-show-ediff PATH DIFF &optional CHAT-BUF REDISPLAY-FN _)

Documentation

Show DIFF for file at PATH using Ediff side-by-side in windows.

If CHAT-BUF is provided it will be used to attempt to re-display the chat buffer after Ediff quits. If REDISPLAY-FN is provided it will be called with CHAT-BUF to perform that re-display (default: display-buffer). ROOTS may be passed for path relativization if desired.

This function tries to be Doom-compatible when Emacs runs Doom popup system (it ignores those popup rules for the generated ediff buffers).

Source Code

;; Defined in /nix/store/jklqzw6pzz6j4jrbv8wy0iwgi7jlrbld-emacs-packages-deps/share/emacs/site-lisp/elpa/eca-20260928.1827/eca-diff.el
(defun eca-diff-show-ediff (path diff &optional chat-buf redisplay-fn _)
  "Show DIFF for file at PATH using Ediff side-by-side in windows.
If CHAT-BUF is provided it will be used to attempt to re-display the
chat buffer after Ediff quits.  If REDISPLAY-FN is provided it will be
called with CHAT-BUF to perform that re-display (default: `display-buffer').
ROOTS may be passed for path relativization if desired.

This function tries to be Doom-compatible when Emacs runs Doom popup
system (it ignores those popup rules for the generated ediff buffers)."
  (let* (;; ediff expects `\n` line endings.
         (diff (replace-regexp-in-string "\r\n" "\n" diff))
         (parsed        (eca-diff-parse-unified-diff diff))
         (orig          (plist-get parsed :original))
         (new           (plist-get parsed :new))
         (buf-orig      (generate-new-buffer (format "*eca-diff-orig:%s*" path)))
         (buf-new       (generate-new-buffer (format "*eca-diff-new:%s*" path)))
         (chat-buf      chat-buf)
         (redisplay-fn  (or redisplay-fn #'eca-diff--default-redisplay-fn))
         ;; Doom-specific: Check if we're in a popup context
         (doom-popup-p   (and (boundp '+popup-buffer-mode) +popup-buffer-mode))
         (in-doom-p      (featurep 'doom))
         ;; Don't save window config if in Doom popup - causes conflicts
         (cwc            (unless doom-popup-p (current-window-configuration)))
         (orig-selected  (selected-window))
         (orig-buffer    chat-buf)
         (ediff-buffers-before
          (seq-filter (lambda (b)
                        (string-match-p "\\*\\(ediff-\\|Ediff Control\\)"
                                        (buffer-name b)))
                      (buffer-list)))
         session-ediff-buffers
         cleanup-fn
         after-setup-fn)

    ;; Doom-specific: Set popup rules to prevent interference
    (when in-doom-p
      (when (fboundp 'set-popup-rule!)
        ;; Prevent ECA diff buffers from being managed by Doom's popup system
        (set-popup-rule! "^\\*eca-diff-" :ignore t)
        (set-popup-rule! "^\\*Ediff Control Panel" :ignore t)))

    ;; Fill temporary buffers
    (with-current-buffer buf-orig
      (let ((inhibit-read-only t))
        (erase-buffer)
        (insert orig)
        (set-buffer-modified-p nil)))
    (with-current-buffer buf-new
      (let ((inhibit-read-only t))
        (erase-buffer)
        (insert new)
        (set-buffer-modified-p nil)))

    ;; Only manipulate windows if not in Doom popup context
    (unless doom-popup-p
      ;; Temporarily relax side-window protections so Ediff can split
      (let ((frame (selected-frame)))
        (dolist (w (seq-filter (lambda (w)
                                 (and (eq (window-frame w) frame)
                                      (or (window-parameter w 'no-delete-other-windows)
                                          (window-parameter w 'window-side))))
                               (window-list)))
          (when (window-live-p w)
            (set-window-parameter w 'no-delete-other-windows nil)
            (set-window-parameter w 'window-side nil))))
      ;; Ensure Ediff has a single full window to manage
      (unless (one-window-p t)
        (delete-other-windows)))

    ;; Enhanced cleanup with Doom compatibility
    (setq cleanup-fn
          (lambda ()
            ;; Kill temp buffers first to avoid conflicts
            (when (buffer-live-p buf-orig) (kill-buffer buf-orig))
            (when (buffer-live-p buf-new)  (kill-buffer buf-new))
            ;; Kill any additional Ediff-generated buffers
            (dolist (b session-ediff-buffers)
              (when (and b (buffer-live-p b))
                (kill-buffer b)))

            ;; Doom-aware window restoration
            (cond
             ;; If we were in a Doom popup, use Doom's restoration
             (doom-popup-p
              (cond
               ((and (fboundp '+popup/restore) (buffer-live-p orig-buffer))
                (+popup/restore))
               ((buffer-live-p orig-buffer)
                (switch-to-buffer orig-buffer))))

             ;; Standard window config restoration with Doom-compatible delay
             (cwc
              ;; Use longer delay for Doom compatibility
              (run-with-timer (if in-doom-p 0.3 0.1) nil
                              (lambda ()
                                (condition-case err
                                    (progn
                                      (set-window-configuration cwc)
                                      (when (window-live-p orig-selected)
                                        (select-window orig-selected))
                                      ;; Re-display chat buffer in side window if needed
                                      (when (and (buffer-live-p chat-buf)
                                                 (or (not (get-buffer-window chat-buf t))
                                                     (not (window-parameter (get-buffer-window chat-buf t) 'window-side))))
                                        (ignore-errors (funcall redisplay-fn chat-buf))))
                                  (error
                                   (message "ECA: Could not restore window config: %s" err)
                                   ;; Fallback: ensure chat buffer is visible
                                   (when (buffer-live-p chat-buf)
                                     (ignore-errors (funcall redisplay-fn chat-buf))))))))

             ;; Fallback for cases without saved config
             (t
              (when (and (buffer-live-p chat-buf)
                         (or (not (get-buffer-window chat-buf t))
                             (not (window-parameter (get-buffer-window chat-buf t) 'window-side))))
                (ignore-errors (funcall redisplay-fn chat-buf)))))

            (remove-hook 'ediff-quit-hook cleanup-fn)))

    ;; After-setup hook: capture ediff buffers and move to first diff
    (setq after-setup-fn
          (lambda ()
            (let ((ediff-buffers-after
                   (seq-filter (lambda (b)
                                 (string-match-p "\\*\\(ediff-\\|Ediff\n Control\\)" (buffer-name b)))
                               (buffer-list))))
              (setq session-ediff-buffers
                    (seq-filter (lambda (b)
                                  (and (not (member b ediff-buffers-before))
                                       (not (string-match-p "*Ediff\n Registry*" (buffer-name b)))))
                                ediff-buffers-after)))
            (condition-case _err
                (progn
                  (setq ediff-current-difference -1)
                  (ediff-next-difference))
              (error nil))
            (remove-hook 'ediff-after-setup-windows-hook after-setup-fn)))

    ;; Install hooks
    (add-hook 'ediff-quit-hook cleanup-fn)
    (add-hook 'ediff-after-setup-windows-hook after-setup-fn)

    ;; Start Ediff with error handling
    (condition-case err
	(let ((ediff-window-setup-function
	       (if (daemonp)
		   'ediff-setup-windows-plain
		 ediff-window-setup-function)))
        (ediff-buffers buf-orig buf-new))
      (error
       ;; On error, remove hooks and restore windows
       (remove-hook 'ediff-quit-hook cleanup-fn)
       (remove-hook 'ediff-after-setup-windows-hook after-setup-fn)

       ;; Doom-aware error recovery
       (cond
        (cwc (set-window-configuration cwc))
        (doom-popup-p
         (when (buffer-live-p orig-buffer)
           (switch-to-buffer orig-buffer))))

       ;; Re-display chat buffer on error
       (when (and (buffer-live-p chat-buf)
                  (or (not (get-buffer-window chat-buf t))
                      (not (window-parameter (get-buffer-window chat-buf t) 'window-side))))
         (ignore-errors (funcall redisplay-fn chat-buf)))

       ;; Kill temp buffers
       (when (buffer-live-p buf-orig) (kill-buffer buf-orig))
       (when (buffer-live-p buf-new)  (kill-buffer buf-new))
       (message "eca-diff: error starting ediff: %s" err)))))