2002-01-07 Michael Kifer <kifer@cs.stonybrook.edu>
[bpt/emacs.git] / lisp / ediff-mult.el
index bb6f815..b8e576d 100644 (file)
@@ -1,8 +1,8 @@
 ;;; ediff-mult.el --- support for multi-file/multi-buffer processing in Ediff
 
-;; Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
+;; Copyright (C) 1995, 96, 97, 98, 99, 2000, 01, 02 Free Software Foundation, Inc.
 
-;; Author: Michael Kifer <kifer@cs.sunysb.edu>
+;; Author: Michael Kifer <kifer@cs.stonybrook.edu>
 
 ;; This file is part of GNU Emacs.
 
@@ -26,7 +26,7 @@
 ;; Users are encouraged to add functionality to this file.
 ;; The present file contains all the infrastructure needed for that.
 ;;
-;; Generally, to to implement a new multisession capability within Ediff,
+;; Generally, to implement a new multisession capability within Ediff,
 ;; you need to tell it 
 ;;
 ;;     1. How to display the session group buffer.
@@ -49,7 +49,7 @@
 ;;        or string).  The function ediff-redraw-registry-buffer displays the
 ;;        second through last of these in the registry buffer. 
 ;;        Also, keep in mind that the function ediff-prepare-meta-buffer
-;;        (which see) prepends the session group buffer to the descriptor and
+;;        (which see) prepends the session group buffer to the descriptor, and
 ;;        nil in front of each subsequent list (i.e., the above list 
 ;;        will become
 ;;              ((meta-buf descriptor) (nil obj1 obj2 obj3) (nil ...) ...)
@@ -187,7 +187,15 @@ This can be toggled with `ediff-toggle-filename-truncation'."
   "*Hooks run just after the registry control panel is set up."
   :type 'hook
   :group 'ediff-mult)
-(defcustom ediff-session-group-setup-hook nil
+
+(defcustom ediff-before-session-group-setup-hooks nil
+  "*Hooks to run before Ediff arranges the window for group-level operations.
+It is used by commands such as ediff-directories.
+This hook can be used to save the previous window config, which can be restored
+on ediff-quit, ediff-suspend, or ediff-quit-session-group-hook."
+  :type 'hook
+  :group 'ediff-hook) 
+(defcustom ediff-after-session-group-setup-hook nil
   "*Hooks run just after a meta-buffer controlling a session group, such as
 ediff-directories, is run."
   :type 'hook
@@ -217,6 +225,13 @@ buffers."
 
 ;;; API for ediff-meta-list
 
+;; Structure of the meta-list:
+;; (HEADER SESSION1 SESSION2 ...)
+;;    HEADER: (GROUP-BUF REGEXP OBJA OBJB OBJC SAVE-DIR)
+;;               OBJA - first directory
+;;               OBJB - second directory
+;;               OBJC - third directory
+;; SESSION1/2/... are described below
 ;; group buffer/regexp
 (defsubst ediff-get-group-buffer (meta-list)
   (nth 0 (car meta-list)))
@@ -233,6 +248,13 @@ buffers."
 (defsubst ediff-get-group-merge-autostore-dir (meta-list)
   (nth 5 (car meta-list)))
 
+;; ELT is a session meta descriptor (what is being preserved as
+;; 'ediff-meta-info)
+;;  The structure is:  (SESSION-CTL-BUFFER STATUS OBJA OBJB OBJC)
+;;   STATUS is ?I, ?*, ?H
+;;   OBJA/B/C is (FILENAME EQSTATUS)
+;;     EQSTATUS is ?= or nil (?= means that this file is equal to some other
+;;                                    file in this session)
 ;; session buffer
 (defsubst ediff-get-session-buffer (elt)
   (nth 0 elt))
@@ -706,7 +728,7 @@ behavior."
 
       (if (eq ediff-metajob-name 'ediff-registry)
          (run-hooks 'ediff-registry-setup-hook)
-       (run-hooks 'ediff-session-group-setup-hook))
+       (run-hooks 'ediff-after-session-group-setup-hook))
       ) ; eval in meta-buffer
     meta-buffer))
 
@@ -793,9 +815,10 @@ behavior."
       (erase-buffer)
       ;; delete phony overlays that used to represent sessions before the buff
       ;; was redrawn
-      (if ediff-emacs-p
-         (mapcar 'delete-overlay (overlays-in 1 1))
-       (map-extents 'delete-extent))
+      (ediff-cond-compile-for-xemacs-or-emacs
+       (map-extents 'delete-extent)   ; xemacs
+       (mapcar 'delete-overlay (overlays-in 1 1))  ; emacs
+       )
       
       (insert (format ediff-meta-buffer-message
                      (ediff-abbrev-jobname ediff-metajob-name)))
@@ -896,30 +919,32 @@ behavior."
 (defun ediff-update-session-marker-in-dir-meta-buffer (session-num)
   (let (buffer-meta-overlays session-info overl buffer-read-only)
     (setq overl
-         (if ediff-xemacs-p
-             (map-extents
-              (lambda (ext maparg)
-                (if (and
-                     (ediff-overlay-get ext 'ediff-meta-info)
-                     (eq (ediff-overlay-get ext 'ediff-meta-session-number)
-                         session-num))
-                    ext)))
+         (ediff-cond-compile-for-xemacs-or-emacs
+          (map-extents ; xemacs
+           (lambda (ext maparg)
+             (if (and
+                  (ediff-overlay-get ext 'ediff-meta-info)
+                  (eq (ediff-overlay-get ext 'ediff-meta-session-number)
+                      session-num))
+                 ext)))
            ;; Emacs doesn't have map-extents, so try harder
            ;; Splice overlay lists to get all buffer overlays
-           (setq buffer-meta-overlays (overlay-lists)
-                 buffer-meta-overlays (append (car buffer-meta-overlays)
-                                              (cdr buffer-meta-overlays)))
-           (car
-            (delq nil
-                  (mapcar
-                   (lambda (overl)
-                     (if (and
-                          (ediff-overlay-get overl 'ediff-meta-info)
-                          (eq (ediff-overlay-get
-                               overl 'ediff-meta-session-number)
-                              session-num))
-                         overl))
-                   buffer-meta-overlays)))))
+          (progn
+            (setq buffer-meta-overlays (overlay-lists)
+                  buffer-meta-overlays (append (car buffer-meta-overlays)
+                                               (cdr buffer-meta-overlays)))
+            (car
+             (delq nil
+                   (mapcar
+                    (lambda (overl)
+                      (if (and
+                           (ediff-overlay-get overl 'ediff-meta-info)
+                           (eq (ediff-overlay-get
+                                overl 'ediff-meta-session-number)
+                               session-num))
+                          overl))
+                    buffer-meta-overlays))))
+          ))
     (or overl
        (error
         "Bug in ediff-update-session-marker-in-dir-meta-buffer: no overlay with given number %S"
@@ -962,7 +987,7 @@ behavior."
                              500))
        file-modtime file-size)
     (cond ((not (stringp fname)) (setq file-size -2)) ; file doesn't exits
-         ((not (ediff-file-remote-p fname))
+         ((ediff-listable-file fname)
           (if (file-exists-p fname)
               ;; set real size and modtime
               (setq file-size (ediff-file-size fname)
@@ -1157,9 +1182,10 @@ Useful commands:
       (erase-buffer)
       ;; delete phony overlays that used to represent sessions before the buff
       ;; was redrawn
-      (if ediff-emacs-p
-         (mapcar 'delete-overlay (overlays-in 1 1))
-       (map-extents 'delete-extent))
+      (ediff-cond-compile-for-xemacs-or-emacs
+       (map-extents 'delete-extent) ; xemacs
+       (mapcar 'delete-overlay (overlays-in 1 1)) ; emacs
+       )
 
       (insert "This is a registry of all active Ediff sessions.
 
@@ -1255,6 +1281,9 @@ Useful commands:
 ;; Sets overlay around a meta record with 'ediff-meta-info property PROP
 ;; If optional SESSION-NUMBER, make it a property of the overlay,
 ;; ediff-meta-session-number
+;; PROP is either the ctl or meta buffer (used when we work with the registry)
+;; or a session meta descriptor of the form
+;;                 (SESSION-CTL-BUFFER STATUS OBJA OBJB OBJC)
 (defun ediff-set-meta-overlay (b e prop &optional session-number hidden)
   (let (overl)
     (setq overl (ediff-make-overlay b e))
@@ -1482,7 +1511,7 @@ all marked sessions must be active."
              
 ;; This function executes in meta buffer.  It knows where event happened.
 (defun ediff-filegroup-action ()
-  "Execute appropriate action for the selected session."
+  "Execute appropriate action for a selected session."
   (interactive)
   (let* ((pos (ediff-event-point last-command-event))
         (meta-buf (ediff-event-buffer last-command-event))
@@ -1525,14 +1554,14 @@ all marked sessions must be active."
                ediff-session-action-function
                ediff-metajob-name 
                ;; make it update (car info) after startup
-               (` (list (lambda () 
-                          ;; child session group should know its parent
-                          (setq ediff-parent-meta-buffer
-                                (quote (, ediff-meta-buffer))
-                                ediff-meta-session-number
-                                (, session-number))
-                          ;; and parent will know its child
-                          (setcar (quote (, info)) ediff-meta-buffer)))))))
+               `(list (lambda () 
+                        ;; child session group should know its parent
+                        (setq ediff-parent-meta-buffer
+                              (quote ,ediff-meta-buffer)
+                              ediff-meta-session-number
+                              ,session-number)
+                        ;; and parent will know its child
+                        (setcar (quote ,info) ediff-meta-buffer))))))
 
            ;; Do ediff-revision on a subdirectory
            ((and (ediff-one-filegroup-metajob)
@@ -1546,15 +1575,15 @@ all marked sessions must be active."
                file1 regexp
                ediff-session-action-function ediff-metajob-name
                ;; make it update (car info) after startup
-               (` (list (lambda () 
-                          ;; child session group should know its parent and
-                          ;; its number
-                          (setq ediff-parent-meta-buffer
-                                (quote (, ediff-meta-buffer))
-                                ediff-meta-session-number
-                                (, session-number))
-                          ;; and parent will know its child
-                          (setcar (quote (, info)) ediff-meta-buffer)))))))
+               `(list (lambda () 
+                        ;; child session group should know its parent and
+                        ;; its number
+                        (setq ediff-parent-meta-buffer
+                              (quote ,ediff-meta-buffer)
+                              ediff-meta-session-number
+                              ,session-number)
+                        ;; and parent will know its child
+                        (setcar (quote ,info) ediff-meta-buffer))))))
 
            ;; From here on---only individual session handlers
 
@@ -1571,124 +1600,121 @@ all marked sessions must be active."
                 (ediff-merge-files
                  file1 file2
                  ;; provide startup hooks 
-                 (` (list (lambda () 
+                 `(list (lambda () 
                             (add-hook
                              'ediff-after-quit-hook-internal
                              (lambda ()
-                               (if (ediff-buffer-live-p (, (current-buffer)))
+                               (if (ediff-buffer-live-p ,(current-buffer))
                                    (ediff-show-meta-buffer
-                                    (, (current-buffer)) (, session-number))))
+                                    ,(current-buffer) ,session-number)))
                              nil 'local)
-                            (setq ediff-meta-buffer (, (current-buffer))
+                            (setq ediff-meta-buffer ,(current-buffer)
                                   ediff-meta-session-number
-                                  (, session-number))
+                                  ,session-number)
                             (setq ediff-merge-store-file
-                                  (, (if (ediff-nonempty-string-p
-                                          merge-autostore-dir)
-                                         (concat
-                                          merge-autostore-dir
-                                          "merge_"
-                                          (file-name-nondirectory file1)))
+                                  ,(if (ediff-nonempty-string-p
+                                        merge-autostore-dir)
+                                       (concat
+                                        merge-autostore-dir
+                                        ediff-merge-filename-prefix
+                                        (file-name-nondirectory file1))
                                      ))
                             ;; make ediff-startup pass
                             ;; ediff-control-buffer back to the meta
                             ;; level; see below
                             (setcar
-                             (quote (, info)) ediff-control-buffer)))))
+                             (quote ,info) ediff-control-buffer))))
               (error "Aborted")))
            ((ediff-one-filegroup-metajob)      ; needs 1 file arg
             (funcall ediff-session-action-function
                      file1
                      ;; provide startup hooks 
-                     (` (list (lambda () 
-                                (add-hook
-                                 'ediff-after-quit-hook-internal
-                                 (lambda ()
-                                   (if (ediff-buffer-live-p
-                                        (, (current-buffer)))
-                                       (ediff-show-meta-buffer
-                                        (, (current-buffer))
-                                        (, session-number))))
-                                 nil 'local)
-                                (setq ediff-meta-buffer (, (current-buffer))
-                                      ediff-meta-session-number
-                                      (, session-number))
-                                (setq ediff-merge-store-file
-                                      (, (if (ediff-nonempty-string-p
-                                              merge-autostore-dir)
-                                             (concat
-                                              merge-autostore-dir
-                                              "merge_"
-                                              (file-name-nondirectory file1)))
-                                         ))
-                                ;; make ediff-startup pass
-                                ;; ediff-control-buffer back to the meta
-                                ;; level; see below
-                                (setcar
-                                 (quote (, info)) ediff-control-buffer))))))
+                     `(list (lambda () 
+                              (add-hook
+                               'ediff-after-quit-hook-internal
+                               (lambda ()
+                                 (if (ediff-buffer-live-p
+                                      ,(current-buffer))
+                                     (ediff-show-meta-buffer
+                                      ,(current-buffer)
+                                      ,session-number)))
+                               nil 'local)
+                              (setq ediff-meta-buffer ,(current-buffer)
+                                    ediff-meta-session-number
+                                    ,session-number)
+                              (setq ediff-merge-store-file
+                                    ,(if (ediff-nonempty-string-p
+                                          merge-autostore-dir)
+                                         (concat
+                                          merge-autostore-dir
+                                          ediff-merge-filename-prefix
+                                          (file-name-nondirectory file1))) )
+                              ;; make ediff-startup pass
+                              ;; ediff-control-buffer back to the meta
+                              ;; level; see below
+                              (setcar
+                               (quote ,info) ediff-control-buffer)))))
            ((not (ediff-metajob3))      ; need 2 file args
             (funcall ediff-session-action-function
                      file1 file2
                      ;; provide startup hooks 
-                     (` (list (lambda () 
-                                (add-hook
-                                 'ediff-after-quit-hook-internal
-                                 (lambda ()
-                                   (if (ediff-buffer-live-p
-                                        (, (current-buffer)))
-                                       (ediff-show-meta-buffer
-                                        (, (current-buffer))
-                                        (, session-number))))
-                                 nil 'local)
-                                (setq ediff-meta-buffer (, (current-buffer))
-                                      ediff-meta-session-number
-                                      (, session-number))
-                                (setq ediff-merge-store-file
-                                      (, (if (ediff-nonempty-string-p
-                                              merge-autostore-dir)
-                                             (concat
-                                              merge-autostore-dir
-                                              "merge_"
-                                              (file-name-nondirectory file1)))
-                                         ))
-                                ;; make ediff-startup pass
-                                ;; ediff-control-buffer back to the meta
-                                ;; level; see below
-                                (setcar
-                                 (quote (, info)) ediff-control-buffer))))))
+                     `(list (lambda () 
+                              (add-hook
+                               'ediff-after-quit-hook-internal
+                               (lambda ()
+                                 (if (ediff-buffer-live-p
+                                      ,(current-buffer))
+                                     (ediff-show-meta-buffer
+                                      ,(current-buffer)
+                                      ,session-number)))
+                               nil 'local)
+                              (setq ediff-meta-buffer ,(current-buffer)
+                                    ediff-meta-session-number
+                                    ,session-number)
+                              (setq ediff-merge-store-file
+                                    ,(if (ediff-nonempty-string-p
+                                          merge-autostore-dir)
+                                         (concat
+                                          merge-autostore-dir
+                                          ediff-merge-filename-prefix
+                                          (file-name-nondirectory file1))) )
+                              ;; make ediff-startup pass
+                              ;; ediff-control-buffer back to the meta
+                              ;; level; see below
+                              (setcar
+                               (quote ,info) ediff-control-buffer)))))
            ((ediff-metajob3)      ; need 3 file args
             (funcall ediff-session-action-function
                      file1 file2 file3
                      ;; arrange startup hooks 
-                     (` (list (lambda () 
-                                (add-hook
-                                 'ediff-after-quit-hook-internal
-                                 (lambda ()
-                                   (if (ediff-buffer-live-p
-                                        (, (current-buffer)))
-                                       (ediff-show-meta-buffer
-                                        (, (current-buffer))
-                                        (, session-number))))
-                                 nil 'local)
-                                (setq ediff-merge-store-file
-                                      (, (if (ediff-nonempty-string-p
-                                              merge-autostore-dir)
-                                             (concat
-                                              merge-autostore-dir
-                                              "merge_"
-                                              (file-name-nondirectory file1)))
-                                         ))
-                                (setq ediff-meta-buffer (, (current-buffer))
-                                      ediff-meta-session-number
-                                      (, session-number))
-                                ;; this arranges that ediff-startup will pass
-                                ;; the value of ediff-control-buffer back to
-                                ;; the meta level, to the record in the meta
-                                ;; list containing the information about the
-                                ;; session associated with that
-                                ;; ediff-control-buffer
-                                (setcar
-                                 (quote (, info)) ediff-control-buffer))))))
+                     `(list (lambda () 
+                              (add-hook
+                               'ediff-after-quit-hook-internal
+                               (lambda ()
+                                 (if (ediff-buffer-live-p
+                                      ,(current-buffer))
+                                     (ediff-show-meta-buffer
+                                      ,(current-buffer)
+                                      ,session-number)))
+                               nil 'local)
+                              (setq ediff-merge-store-file
+                                    ,(if (ediff-nonempty-string-p
+                                          merge-autostore-dir)
+                                         (concat
+                                          merge-autostore-dir
+                                          ediff-merge-filename-prefix
+                                          (file-name-nondirectory file1))) )
+                              (setq ediff-meta-buffer , (current-buffer)
+                                    ediff-meta-session-number
+                                    ,session-number)
+                              ;; this arranges that ediff-startup will pass
+                              ;; the value of ediff-control-buffer back to
+                              ;; the meta level, to the record in the meta
+                              ;; list containing the information about the
+                              ;; session associated with that
+                              ;; ediff-control-buffer
+                              (setcar
+                               (quote ,info) ediff-control-buffer)))))
            ) ; cond
       ) ; eval in meta-buf
     ))
@@ -1722,6 +1748,7 @@ all marked sessions must be active."
 (defun ediff-show-meta-buffer (&optional meta-buf session-number)
   "Show the session group buffer."
   (interactive)
+  (run-hooks 'ediff-before-directory-setup-hooks)
   (let (wind frame silent)
     (if meta-buf (setq silent t))
 
@@ -1772,6 +1799,8 @@ all marked sessions must be active."
          (setq frame (window-frame wind))
          (raise-frame frame)
          (ediff-reset-mouse frame)))
+    (sit-for 0) ; sometimes needed to synch the display and ensure that the
+               ; point ends up after the just completed session
     (run-hooks 'ediff-show-session-group-hook)
     ))
 
@@ -1952,18 +1981,22 @@ If this is a session registry buffer then just bury it."
   (let (result olist tmp)
     (if (and point (ediff-buffer-live-p buf))
        (ediff-with-current-buffer buf
-         (if ediff-xemacs-p
-             (setq result
-                   (if (setq tmp (extent-at point buf 'ediff-meta-info))
-                       (ediff-overlay-get tmp 'ediff-meta-info)))
-           (setq olist (overlays-at point))
-           (setq olist
-                 (mapcar (lambda (elt) (overlay-get elt 'ediff-meta-info))
-                         olist))
-           (while (and olist (null (car olist))
-                       (overlay-get (car olist) 'invisible))
-             (setq olist (cdr olist)))
-           (setq result (car olist)))))
+         (ediff-cond-compile-for-xemacs-or-emacs
+          (setq result  ; xemacs
+                (if (setq tmp (extent-at point buf 'ediff-meta-info))
+                    (ediff-overlay-get tmp 'ediff-meta-info)))
+          (progn ; emacs
+            (setq olist (overlays-at point))
+            (setq olist
+                  (mapcar (lambda (elt)
+                            (unless (overlay-get elt 'invisible)
+                              (overlay-get elt 'ediff-meta-info)))
+                          olist))
+            (while (and olist (null (car olist)))
+              (setq olist (cdr olist)))
+            (setq result (car olist)))
+          )
+         ))
     (if result
        result
       (if noerror
@@ -1973,14 +2006,17 @@ If this is a session registry buffer then just bury it."
 
 
 (defun ediff-get-meta-overlay-at-pos (point)
-  (if ediff-xemacs-p
-      (extent-at point (current-buffer) 'ediff-meta-info)
-    (let* ((overl-list (overlays-at point))
-          (overl (car overl-list)))
-      (while (and overl (null (overlay-get overl 'ediff-meta-info)))
-       (setq overl-list (cdr overl-list)
-             overl (car overl-list)))
-      overl)))
+  (ediff-cond-compile-for-xemacs-or-emacs
+   (extent-at point (current-buffer) 'ediff-meta-info) ; xemacs
+   ;; emacs
+   (let* ((overl-list (overlays-at point))
+         (overl (car overl-list)))
+     (while (and overl (null (overlay-get overl 'ediff-meta-info)))
+       (setq overl-list (cdr overl-list)
+            overl (car overl-list)))
+     overl)
+   )
+  )
 
 (defsubst ediff-get-session-number-at-pos (point &optional meta-buffer)
   (setq meta-buffer (if (ediff-buffer-live-p meta-buffer)
@@ -1996,18 +2032,21 @@ If this is a session registry buffer then just bury it."
   (if (eobp)
       (goto-char (point-min))
     (let ((overl (ediff-get-meta-overlay-at-pos point)))
-      (if ediff-xemacs-p
-         (progn
-           (if overl
-               (setq overl (next-extent overl))
-             (setq overl (next-extent (current-buffer))))
-           (if overl
-               (extent-start-position overl)
-             (point-max)))
-       (if overl
-           ;; note: end of current overlay is the beginning of the next one
-           (overlay-end overl)
-         (next-overlay-change point))))
+      (ediff-cond-compile-for-xemacs-or-emacs
+       (progn ; xemacs
+        (if overl
+            (setq overl (next-extent overl))
+          (setq overl (next-extent (current-buffer))))
+        (if overl
+            (extent-start-position overl)
+          (point-max)))
+       ;; emacs
+       (if overl
+          ;; note: end of current overlay is the beginning of the next one
+          (overlay-end overl)
+        (next-overlay-change point))
+       )
+      )
     ))
 
 
@@ -2015,27 +2054,30 @@ If this is a session registry buffer then just bury it."
   (if (bobp)
       (goto-char (point-max))
     (let ((overl (ediff-get-meta-overlay-at-pos point)))
-      (if ediff-xemacs-p
-         (progn
-           (if overl
-               (setq overl (previous-extent overl))
-             (setq overl (previous-extent (current-buffer))))
-           (if overl
-               (extent-start-position overl)
-             (point-min)))
-       (if overl (setq point (overlay-start overl)))
-       ;; to get to the beginning of prev overlay
-       (if (not (bobp))
-           ;; trick to overcome an emacs bug--doesn't always find previous
-           ;; overlay change correctly
-           (setq point (1- point)))
-       (setq point (previous-overlay-change point))
-       ;; If we are not over an overlay after subtracting 1, it means we are
-       ;; in the description area preceding session records.  In this case,
-       ;; goto the top of the registry buffer.
-       (or (car (overlays-at point))
-           (setq point (point-min)))
-       point))))
+      (ediff-cond-compile-for-xemacs-or-emacs
+       (progn
+        (if overl
+            (setq overl (previous-extent overl))
+          (setq overl (previous-extent (current-buffer))))
+        (if overl
+            (extent-start-position overl)
+          (point-min)))
+       (progn
+        (if overl (setq point (overlay-start overl)))
+        ;; to get to the beginning of prev overlay
+        (if (not (bobp))
+            ;; trick to overcome an emacs bug--doesn't always find previous
+            ;; overlay change correctly
+            (setq point (1- point)))
+        (setq point (previous-overlay-change point))
+        ;; If we are not over an overlay after subtracting 1, it means we are
+        ;; in the description area preceding session records.  In this case,
+        ;; goto the top of the registry buffer.
+        (or (car (overlays-at point))
+            (setq point (point-min)))
+        point)
+       )
+      )))
 
 ;; this is the action invoked when the user selects a patch from the meta
 ;; buffer.
@@ -2086,10 +2128,17 @@ If this is a session registry buffer then just bury it."
   (ediff-update-meta-buffer (current-buffer) 'must-redraw))
 
 
-(defun ediff-meta-mark-equal-files ()
-  "Run though the session list and mark identical files.
-This is used only for sessions that involve 2 or 3 files at the same time."
+;; ACTION is ?h, ?m, ?=: to mark for hiding, mark for operation, or simply
+;; indicate which are equal files
+(defun ediff-meta-mark-equal-files (&optional action)
+  "Run through the session list and mark identical files.
+This is used only for sessions that involve 2 or 3 files at the same time.
+ACTION is an optional argument that can be ?h, ?m, ?=, to mark for hiding, mark
+for operation, or simply indicate which are equal files. If it is nil, then
+last-command-char is used to decide which action to take."
   (interactive)
+  (if (null action)
+      (setq action last-command-char))
   (let ((list (cdr ediff-meta-list))
        marked1 marked2 marked3
        fileinfo1 fileinfo2 fileinfo3 elt)
@@ -2114,9 +2163,9 @@ This is used only for sessions that involve 2 or 3 files at the same time."
            (or (ediff-mark-if-equal fileinfo2 fileinfo3)
                (setq marked3 nil))))
       (if (and marked1 marked2 marked3)
-         (cond ((eq last-command-char ?h)
+         (cond ((eq action ?h)
                 (ediff-mark-session-for-hiding elt 'mark))
-               ((eq last-command-char ?m)
+               ((eq action ?m)
                 (ediff-mark-session-for-operation elt 'mark))
                ))
       (setq list (cdr list)))