Specify CHARSET-ID explicitely for private charsets.
[bpt/emacs.git] / lisp / ediff-mult.el
CommitLineData
eb4daa01 1;;; ediff-mult.el --- support for multi-file/multi-buffer processing in Ediff
b578f267 2
ddc90f39 3;; Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
fa2eb9ac
MK
4
5;; Author: Michael Kifer <kifer@cs.sunysb.edu>
6
7;; This file is part of GNU Emacs.
8
9;; GNU Emacs is free software; you can redistribute it and/or modify
10;; it under the terms of the GNU General Public License as published by
11;; the Free Software Foundation; either version 2, or (at your option)
12;; any later version.
13
14;; GNU Emacs is distributed in the hope that it will be useful,
15;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17;; GNU General Public License for more details.
18
19;; You should have received a copy of the GNU General Public License
b578f267
EN
20;; along with GNU Emacs; see the file COPYING. If not, write to the
21;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22;; Boston, MA 02111-1307, USA.
23
24;;; Commentary:
fa2eb9ac 25
bbe6126c
MK
26;; Users are encouraged to add functionality to this file.
27;; The present file contains all the infrastructure needed for that.
fa2eb9ac
MK
28;;
29;; Generally, to to implement a new multisession capability within Ediff,
30;; you need to tell it
31;;
32;; 1. How to display the session group buffer.
33;; This function must indicate which Ediff sessions are active (+) and
34;; which are finished (-).
35;; See ediff-redraw-directory-group-buffer for an example.
36;; In all likelihood, ediff-redraw-directory-group-buffer can be used
37;; directly or after a small modification.
38;; 2. What action to take when the user clicks button 2 or types v,e, or
bbe6126c 39;; RET. See ediff-filegroup-action.
fa2eb9ac
MK
40;; 3. Provide a list of pairs or triples of file names (or buffers,
41;; depending on the particular Ediff operation you want to invoke)
42;; in the following format:
bbe6126c 43;; (descriptor (obj1 obj2 obj3) (...) ...)
fa2eb9ac 44;; Actually, the format of this list is pretty much up to the
3af0304a 45;; developer. The only thing is that it must be a list of lists,
bbe6126c
MK
46;; and the first list must describe the meta session, and subsequent
47;; elements must describe individual sessions.
48;; This descriptor must be a list of two, three, or four elements (nil
3af0304a 49;; or string). The function ediff-redraw-registry-buffer displays the
bbe6126c 50;; second through last of these in the registry buffer.
fa2eb9ac 51;; Also, keep in mind that the function ediff-prepare-meta-buffer
bbe6126c
MK
52;; (which see) prepends the session group buffer to the descriptor and
53;; nil in front of each subsequent list (i.e., the above list
54;; will become
55;; ((meta-buf descriptor) (nil obj1 obj2 obj3) (nil ...) ...)
fa2eb9ac
MK
56;; Ediff expects that your function (in 2 above) will arrange to
57;; replace this prepended nil (via setcar) with the actual ediff
58;; control buffer associated with an appropriate Ediff session.
59;; This is arranged through internal startup hooks that can be passed
60;; to any of Ediff major entries (such as ediff-files, epatch, etc.).
bbe6126c
MK
61;; See how this is done in ediff-filegroup-action.
62;;
63;; Session descriptions are of the form (obj1 obj2 obj3), which
3af0304a
MK
64;; describe objects relevant to the session. Usually they are names of
65;; files, but sometimes they may be other things. For instance, obj3
66;; is nil for jobs that involve only two files. For patch jobs, obj2
67;; and obj3 are markers that specify the patch corresponding to the
68;; file (whose name is obj1).
fa2eb9ac
MK
69;; 4. Write a function that makes a call to ediff-prepare-meta-buffer
70;; passing all this info.
71;; You may be able to use ediff-directories-internal as a template.
72;; 5. If you intend to add several related pieces of functionality,
73;; you may want to keep the function in 4 as an internal version
74;; and then write several top-level interactive functions that call it
75;; with different parameters.
76;; See how ediff-directories, ediff-merge-directories, and
77;; ediff-merge-directories-with-ancestor all use
78;; ediff-directories-internal.
79;;
bbe6126c 80;; A useful addition here could be session groups selected by patterns
3af0304a 81;; (which are different in each directory). For instance, one may want to
fa2eb9ac 82;; compare files of the form abc{something}.c to files old{something}.d
3af0304a 83;; which may be in the same or different directories. Or, one may want to
fa2eb9ac
MK
84;; compare all files of the form {something} to files of the form {something}~.
85;;
009650b3 86;; Implementing this requires writing an collating function, which should pair
3af0304a
MK
87;; up appropriate files. It will also require a generalization of the
88;; functions that do the layout of the meta- and differences buffers and of
bbe6126c 89;; ediff-filegroup-action.
b578f267
EN
90
91;;; Code:
fa2eb9ac 92
ddc90f39
MK
93(provide 'ediff-mult)
94
95(defgroup ediff-mult nil
985d0dad 96 "Multi-file and multi-buffer processing in Ediff"
ddc90f39
MK
97 :prefix "ediff-"
98 :group 'ediff)
99
100
101;; compiler pacifier
102(eval-when-compile
103 (let ((load-path (cons (expand-file-name ".") load-path)))
104 (or (featurep 'ediff-init)
105 (load "ediff-init.el" nil nil 'nosuffix))
106 (or (featurep 'ediff-util)
107 (load "ediff-util.el" nil nil 'nosuffix))
108 ))
109;; end pacifier
110
fa2eb9ac 111(require 'ediff-init)
ddc90f39 112(require 'ediff-util)
fa2eb9ac
MK
113
114;; meta-buffer
115(ediff-defvar-local ediff-meta-buffer nil "")
116(ediff-defvar-local ediff-parent-meta-buffer nil "")
117;; the registry buffer
118(defvar ediff-registry-buffer nil)
119
120(defconst ediff-meta-buffer-message "This is an Ediff Session Group Panel: %s
121
122Useful commands:
328b4b70
MK
123 button2, v, or RET over session record: start that Ediff session
124 M:\tin sessions invoked from here, brings back this group panel
125 R:\tdisplay the registry of active Ediff sessions
126 h:\tmark session for hiding (toggle)
127 x:\thide marked sessions; with prefix arg: unhide
128 m:\tmark session for a non-hiding operation (toggle)
3ec8facd 129 uh/um:\tunmark all sessions marked for hiding/operation
328b4b70
MK
130 n,SPC:\tnext session
131 p,DEL:\tprevious session
132 E:\tbrowse Ediff on-line manual
133 T:\ttoggle truncation of long file names
134 q:\tquit this session group
fa2eb9ac
MK
135")
136
137(ediff-defvar-local ediff-meta-buffer-map nil
138 "The keymap for the meta buffer.")
139(defvar ediff-dir-diffs-buffer-map (make-sparse-keymap)
140 "The keymap to be installed in the buffer showing differences between
141directories.")
142
143;; Variable specifying the action to take when the use invokes ediff in the
3af0304a 144;; meta buffer. This is usually ediff-registry-action or ediff-filegroup-action
fa2eb9ac
MK
145(ediff-defvar-local ediff-meta-action-function nil "")
146;; Tells ediff-update-meta-buffer how to redraw it
147(ediff-defvar-local ediff-meta-redraw-function nil "")
bbe6126c
MK
148;; Tells ediff-filegroup-action and similar procedures how to invoke Ediff for
149;; the sessions in a given session group
fa2eb9ac
MK
150(ediff-defvar-local ediff-session-action-function nil "")
151
152(ediff-defvar-local ediff-metajob-name nil "")
153
154;; buffer used to collect custom diffs from individual sessions in the group
155(ediff-defvar-local ediff-meta-diff-buffer nil "")
156
157;; history var to use for filtering groups
158(defvar ediff-filtering-regexp-history nil "")
159
bd698e98
MK
160;; This has the form ((meta-buf regexp dir1 dir2 dir3 merge-auto-store-dir)
161;; (ctl-buf session-status (file1 . eq-status) (file2 . eq-status) (file3
162;; . eq-status)) (ctl-buf session-status (file1 . eq-status) (file2
163;; . eq-status)) ...)
3af0304a
MK
164;; If ctl-buf is nil, the file-pair hasn't processed yet. If it is
165;; killed-buffer object, the file pair has been processed. If it is a live
bd698e98
MK
166;; buffer, this means ediff is still working on the pair.
167;; Eq-status of a file is t if the file equals some other file in the same
168;; group.
fa2eb9ac
MK
169(ediff-defvar-local ediff-meta-list nil "")
170
328b4b70
MK
171(ediff-defvar-local ediff-meta-session-number nil "")
172
fa2eb9ac
MK
173
174;; the difference list between directories in a directory session group
175(ediff-defvar-local ediff-dir-difference-list nil "")
176(ediff-defvar-local ediff-dir-diffs-buffer nil "")
177
3af0304a 178;; The registry of Ediff sessions. A list of control buffers.
fa2eb9ac
MK
179(defvar ediff-session-registry nil)
180
328b4b70
MK
181(defcustom ediff-meta-truncate-filenames t
182 "*If non-nil, truncate long file names in the session group buffers.
183This can be toggled with `ediff-toggle-filename-truncation'."
184 :type 'hook
185 :group 'ediff-mult)
ddc90f39
MK
186(defcustom ediff-registry-setup-hook nil
187 "*Hooks run just after the registry control panel is set up."
188 :type 'hook
189 :group 'ediff-mult)
190(defcustom ediff-session-group-setup-hook nil
fa2eb9ac 191 "*Hooks run just after a meta-buffer controlling a session group, such as
ddc90f39
MK
192ediff-directories, is run."
193 :type 'hook
194 :group 'ediff-mult)
195(defcustom ediff-quit-session-group-hook nil
196 "*Hooks run just before exiting a session group."
197 :type 'hook
198 :group 'ediff-mult)
199(defcustom ediff-show-registry-hook nil
200 "*Hooks run just after the registry buffer is shown."
201 :type 'hook
202 :group 'ediff-mult)
3af0304a 203(defcustom ediff-show-session-group-hook '(delete-other-windows)
ddc90f39
MK
204 "*Hooks run just after a session group buffer is shown."
205 :type 'hook
206 :group 'ediff-mult)
207(defcustom ediff-meta-buffer-keymap-setup-hook nil
bf5d92c5
MK
208 "*Hooks run just after setting up the ediff-meta-buffer-map.
209This keymap controls key bindings in the meta buffer and is a local variable.
210This means that you can set different bindings for different kinds of meta
ddc90f39
MK
211buffers."
212 :type 'hook
213 :group 'ediff-mult)
fa2eb9ac 214
3af0304a 215;; Buffer holding the multi-file patch. Local to the meta buffer
bbe6126c
MK
216(ediff-defvar-local ediff-meta-patchbufer nil "")
217
218;;; API for ediff-meta-list
fa2eb9ac 219
bbe6126c 220;; group buffer/regexp
bd698e98 221(defsubst ediff-get-group-buffer (meta-list)
fa2eb9ac 222 (nth 0 (car meta-list)))
92c51e07 223
bd698e98 224(defsubst ediff-get-group-regexp (meta-list)
fa2eb9ac 225 (nth 1 (car meta-list)))
bbe6126c 226;; group objects
bd698e98 227(defsubst ediff-get-group-objA (meta-list)
fa2eb9ac 228 (nth 2 (car meta-list)))
bd698e98 229(defsubst ediff-get-group-objB (meta-list)
fa2eb9ac 230 (nth 3 (car meta-list)))
bd698e98 231(defsubst ediff-get-group-objC (meta-list)
fa2eb9ac 232 (nth 4 (car meta-list)))
bd698e98 233(defsubst ediff-get-group-merge-autostore-dir (meta-list)
92c51e07
MK
234 (nth 5 (car meta-list)))
235
bbe6126c 236;; session buffer
bd698e98 237(defsubst ediff-get-session-buffer (elt)
fa2eb9ac 238 (nth 0 elt))
bd698e98 239(defsubst ediff-get-session-status (elt)
fa2eb9ac 240 (nth 1 elt))
bd698e98 241(defsubst ediff-set-session-status (session-info new-status)
bbe6126c
MK
242 (setcar (cdr session-info) new-status))
243;; session objects
bd698e98 244(defsubst ediff-get-session-objA (elt)
fa2eb9ac 245 (nth 2 elt))
bd698e98 246(defsubst ediff-get-session-objB (elt)
fa2eb9ac 247 (nth 3 elt))
bd698e98 248(defsubst ediff-get-session-objC (elt)
fa2eb9ac 249 (nth 4 elt))
bd698e98 250(defsubst ediff-get-session-objA-name (elt)
bbe6126c 251 (car (nth 2 elt)))
bd698e98 252(defsubst ediff-get-session-objB-name (elt)
bbe6126c 253 (car (nth 3 elt)))
bd698e98 254(defsubst ediff-get-session-objC-name (elt)
bbe6126c
MK
255 (car (nth 4 elt)))
256;; equality indicators
257(defsubst ediff-get-file-eqstatus (elt)
258 (nth 1 elt))
259(defsubst ediff-set-file-eqstatus (elt value)
260 (setcar (cdr elt) value))
fa2eb9ac 261
328b4b70
MK
262;; The activity marker is either or + (active session, i.e., ediff is currently
263;; run in it), or - (finished session, i.e., we've ran ediff in it and then
3af0304a 264;; exited). Return nil, if session is neither active nor finished
328b4b70
MK
265(defun ediff-get-session-activity-marker (session)
266 (let ((session-buf (ediff-get-session-buffer session)))
267 (cond ((null session-buf) nil) ; virgin session
268 ((ediff-buffer-live-p session-buf) ?+) ;active session
269 (t ?-))))
270
bd698e98
MK
271;; checks if the session is a meta session
272(defun ediff-meta-session-p (session-info)
273 (and (stringp (ediff-get-session-objA-name session-info))
274 (file-directory-p (ediff-get-session-objA-name session-info))
275 (stringp (ediff-get-session-objB-name session-info))
276 (file-directory-p (ediff-get-session-objB-name session-info))
277 (if (stringp (ediff-get-session-objC-name session-info))
278 (file-directory-p (ediff-get-session-objC-name session-info)) t)))
279
fa2eb9ac
MK
280;; set up the keymap in the meta buffer
281(defun ediff-setup-meta-map()
282 (setq ediff-meta-buffer-map (make-sparse-keymap))
283 (suppress-keymap ediff-meta-buffer-map)
284 (define-key ediff-meta-buffer-map "q" 'ediff-quit-meta-buffer)
328b4b70 285 (define-key ediff-meta-buffer-map "T" 'ediff-toggle-filename-truncation)
fa2eb9ac
MK
286 (define-key ediff-meta-buffer-map "R" 'ediff-show-registry)
287 (define-key ediff-meta-buffer-map "E" 'ediff-documentation)
288 (define-key ediff-meta-buffer-map "v" ediff-meta-action-function)
289 (define-key ediff-meta-buffer-map "\C-m" ediff-meta-action-function)
290 (define-key ediff-meta-buffer-map " " 'ediff-next-meta-item)
328b4b70 291 (define-key ediff-meta-buffer-map "n" 'ediff-next-meta-item)
fa2eb9ac 292 (define-key ediff-meta-buffer-map "\C-?" 'ediff-previous-meta-item)
328b4b70 293 (define-key ediff-meta-buffer-map "p" 'ediff-previous-meta-item)
fa2eb9ac
MK
294 (define-key ediff-meta-buffer-map [delete] 'ediff-previous-meta-item)
295 (define-key ediff-meta-buffer-map [backspace] 'ediff-previous-meta-item)
bbe6126c 296 (or (ediff-one-filegroup-metajob)
3ec8facd
KH
297 (progn
298 (define-key ediff-meta-buffer-map "=" nil)
299 (define-key ediff-meta-buffer-map "==" 'ediff-meta-mark-equal-files)
300 (define-key ediff-meta-buffer-map "=m" 'ediff-meta-mark-equal-files)
301 (define-key ediff-meta-buffer-map "=h" 'ediff-meta-mark-equal-files)))
fa2eb9ac
MK
302 (if ediff-no-emacs-help-in-control-buffer
303 (define-key ediff-meta-buffer-map "\C-h" 'ediff-previous-meta-item))
304 (if ediff-emacs-p
305 (define-key ediff-meta-buffer-map [mouse-2] ediff-meta-action-function)
306 (define-key ediff-meta-buffer-map [button2] ediff-meta-action-function))
307
bf5d92c5
MK
308 (use-local-map ediff-meta-buffer-map)
309 ;; modify ediff-meta-buffer-map here
310 (run-hooks 'ediff-meta-buffer-keymap-setup-hook))
fa2eb9ac
MK
311
312(defun ediff-meta-mode ()
313 "This mode controls all operations on Ediff session groups.
314It is entered through one of the following commands:
315 `ediff-directories'
316 `edirs'
317 `ediff-directories3'
318 `edirs3'
319 `ediff-merge-directories'
320 `edirs-merge'
321 `ediff-merge-directories-with-ancestor'
322 `edirs-merge-with-ancestor'
323 `ediff-directory-revisions'
324 `edir-revisions'
325 `ediff-merge-directory-revisions'
326 `edir-merge-revisions'
327 `ediff-merge-directory-revisions-with-ancestor'
328 `edir-merge-revisions-with-ancestor'
329
330Commands:
331\\{ediff-meta-buffer-map}"
332 (kill-all-local-variables)
333 (setq major-mode 'ediff-meta-mode)
334 (setq mode-name "MetaEdiff"))
335
336
337;; the keymap for the buffer showing directory differences
338(suppress-keymap ediff-dir-diffs-buffer-map)
339(define-key ediff-dir-diffs-buffer-map "q" 'ediff-bury-dir-diffs-buffer)
340(define-key ediff-dir-diffs-buffer-map " " 'next-line)
328b4b70 341(define-key ediff-dir-diffs-buffer-map "n" 'next-line)
fa2eb9ac 342(define-key ediff-dir-diffs-buffer-map "\C-?" 'previous-line)
328b4b70 343(define-key ediff-dir-diffs-buffer-map "p" 'previous-line)
fa2eb9ac
MK
344(define-key ediff-dir-diffs-buffer-map [delete] 'previous-line)
345(define-key ediff-dir-diffs-buffer-map [backspace] 'previous-line)
346
347(defun ediff-next-meta-item (count)
348 "Move to the next item in Ediff registry or session group buffer.
3af0304a 349Moves in circular fashion. With numeric prefix arg, skip this many items."
fa2eb9ac
MK
350 (interactive "p")
351 (or count (setq count 1))
328b4b70
MK
352 (let (overl)
353 (while (< 0 count)
354 (setq count (1- count))
355 (ediff-next-meta-item1)
356 (setq overl (ediff-get-meta-overlay-at-pos (point)))
357 ;; skip invisible ones
358 (while (and overl (ediff-overlay-get overl 'invisible))
359 (ediff-next-meta-item1)
360 (setq overl (ediff-get-meta-overlay-at-pos (point)))))))
fa2eb9ac
MK
361
362;; Move to the next meta item
363(defun ediff-next-meta-item1 ()
364 (let (pos)
365 (setq pos (ediff-next-meta-overlay-start (point)))
fa2eb9ac
MK
366 (if pos (goto-char pos))
367 (if (eq ediff-metajob-name 'ediff-registry)
bbe6126c
MK
368 (if (and (ediff-get-meta-info (current-buffer) pos 'noerror)
369 (search-forward "*Ediff" nil t))
fa2eb9ac
MK
370 (skip-chars-backward "a-zA-Z*"))
371 (if (> (skip-chars-forward "-+?H* \t0-9") 0)
372 (backward-char 1)))))
373
374
375(defun ediff-previous-meta-item (count)
376 "Move to the previous item in Ediff registry or session group buffer.
3af0304a 377Moves in circular fashion. With numeric prefix arg, skip this many items."
fa2eb9ac
MK
378 (interactive "p")
379 (or count (setq count 1))
328b4b70
MK
380 (let (overl)
381 (while (< 0 count)
382 (setq count (1- count))
383 (ediff-previous-meta-item1)
384 (setq overl (ediff-get-meta-overlay-at-pos (point)))
385 ;; skip invisible ones
386 (while (and overl (ediff-overlay-get overl 'invisible))
387 (ediff-previous-meta-item1)
388 (setq overl (ediff-get-meta-overlay-at-pos (point)))))))
fa2eb9ac
MK
389
390(defun ediff-previous-meta-item1 ()
391 (let (pos)
392 (setq pos (ediff-previous-meta-overlay-start (point)))
393;;; ;; skip deleted
394;;; (while (ediff-get-session-status
395;;; (ediff-get-meta-info (current-buffer) pos 'noerror))
396;;; (setq pos (ediff-previous-meta-overlay-start pos)))
397
398 (if pos (goto-char pos))
399 (if (eq ediff-metajob-name 'ediff-registry)
bbe6126c
MK
400 (if (and (ediff-get-meta-info (current-buffer) pos 'noerror)
401 (search-forward "*Ediff" nil t))
fa2eb9ac
MK
402 (skip-chars-backward "a-zA-Z*"))
403 (if (> (skip-chars-forward "-+?H* \t0-9") 0)
bbe6126c
MK
404 (backward-char 1)))
405 ))
fa2eb9ac 406
92c51e07
MK
407(defsubst ediff-add-slash-if-directory (dir file)
408 (if (file-directory-p (concat dir file))
409 (file-name-as-directory file)
410 file))
fa2eb9ac 411
328b4b70
MK
412(defun ediff-toggle-filename-truncation ()
413 "Toggle truncation of long file names in session group buffers.
414Set `ediff-meta-truncate-filenames' variable if you want to change the default
415behavior."
416 (interactive)
417 (setq ediff-meta-truncate-filenames (not ediff-meta-truncate-filenames))
418 (ediff-update-meta-buffer (current-buffer) 'must-redraw))
419
fa2eb9ac 420
3af0304a 421;; DIR1, DIR2, DIR3 are directories. DIR3 can be nil.
92c51e07
MK
422;; OUTPUT-DIR is a directory for auto-storing the results of merge jobs.
423;; Can be nil.
424;; REGEXP is a regexp used to filter out files in the directories.
fa2eb9ac 425;; If a file is a directory in dir1 but not dir2 (or vice versa), it is not
3af0304a 426;; included in the intersection. However, a regular file that is a dir in dir3
fa2eb9ac
MK
427;; is included, since dir3 files are supposed to be ancestors for merging.
428;; Returns a list of the form:
429;; ((dir1 dir2 dir3) (f1 f2 f3) (f1 f2 f3) ...)
430;; dir3, f3 can be nil if intersecting only 2 directories.
3af0304a 431;; If COMPARISON-FUNC is given, use it. Otherwise, use string=
92c51e07
MK
432;; DIFF-VAR contains the name of the variable in which to return the
433;; difference list (which represents the differences among the contents of
3af0304a 434;; directories). The diff list is of the form:
fa2eb9ac
MK
435;; ((dir1 dir2 dir3) (file . num) (file . num)...)
436;; where num encodes the set of dirs where the file is found:
437;; 2 - only dir1; 3 - only dir2; 5 - only dir3; 6 - dir1&2; 10 - dir1&3; etc.
4ae69eac
MK
438(defun ediff-intersect-directories (jobname
439 diff-var regexp dir1 dir2
92c51e07
MK
440 &optional
441 dir3 merge-autostore-dir comparison-func)
fa2eb9ac
MK
442 (setq comparison-func (or comparison-func 'string=))
443 (let (lis1 lis2 lis3 common auxdir1 auxdir2 auxdir3 difflist)
444
445 (setq auxdir1 (file-name-as-directory dir1)
446 lis1 (directory-files auxdir1 nil regexp)
92c51e07
MK
447 lis1 (delete "." lis1)
448 lis1 (delete ".." lis1)
449 lis1 (mapcar
3af0304a
MK
450 (lambda (elt)
451 (ediff-add-slash-if-directory auxdir1 elt))
92c51e07 452 lis1)
fa2eb9ac 453 auxdir2 (file-name-as-directory dir2)
92c51e07 454 lis2 (mapcar
3af0304a
MK
455 (lambda (elt)
456 (ediff-add-slash-if-directory auxdir2 elt))
92c51e07 457 (directory-files auxdir2 nil regexp)))
fa2eb9ac
MK
458
459 (if (stringp dir3)
460 (setq auxdir3 (file-name-as-directory dir3)
92c51e07 461 lis3 (mapcar
3af0304a
MK
462 (lambda (elt)
463 (ediff-add-slash-if-directory auxdir3 elt))
92c51e07
MK
464 (directory-files auxdir3 nil regexp))))
465
3af0304a 466 (if (ediff-nonempty-string-p merge-autostore-dir)
92c51e07
MK
467 (setq merge-autostore-dir
468 (file-name-as-directory merge-autostore-dir)))
4ae69eac 469 (setq common (ediff-intersection lis1 lis2 comparison-func))
92c51e07
MK
470
471 ;; In merge with ancestor jobs, we don't intersect with lis3.
472 ;; If there is no ancestor, we'll offer to merge without the ancestor.
473 ;; So, we intersect with lis3 only when we are doing 3-way file comparison
474 (if (and lis3 (ediff-comparison-metajob3 jobname))
475 (setq common (ediff-intersection common lis3 comparison-func)))
fa2eb9ac 476
4ae69eac
MK
477 ;; copying is needed because sort sorts via side effects
478 (setq common (sort (ediff-copy-list common) 'string-lessp))
fa2eb9ac
MK
479
480 ;; compute difference list
4ae69eac
MK
481 (setq difflist (ediff-set-difference
482 (ediff-union (ediff-union lis1 lis2 comparison-func)
483 lis3
484 comparison-func)
fa2eb9ac 485 common
4ae69eac 486 comparison-func)
fa2eb9ac 487 difflist (delete "." difflist)
4ae69eac
MK
488 ;; copying is needed because sort sorts via side effects
489 difflist (sort (ediff-copy-list (delete ".." difflist))
490 'string-lessp))
fa2eb9ac 491
3af0304a 492 (setq difflist (mapcar (lambda (elt) (cons elt 1)) difflist))
fa2eb9ac
MK
493
494 ;; check for files belonging to lis1/2/3
3af0304a
MK
495 (mapcar (lambda (elt)
496 (if (member (car elt) lis1)
497 (setcdr elt (* (cdr elt) 2)))
498 (if (member (car elt) lis2)
499 (setcdr elt (* (cdr elt) 3)))
500 (if (member (car elt) lis3)
501 (setcdr elt (* (cdr elt) 5)))
502 )
fa2eb9ac
MK
503 difflist)
504 (setq difflist (cons (list regexp auxdir1 auxdir2 auxdir3) difflist))
505
92c51e07 506 ;; return the difference list back to the calling function
fa2eb9ac
MK
507 (set diff-var difflist)
508
509 ;; return result
92c51e07
MK
510 (cons (list regexp auxdir1 auxdir2 auxdir3 merge-autostore-dir)
511 (mapcar
3af0304a
MK
512 (lambda (elt)
513 (list (concat auxdir1 elt)
514 (concat auxdir2 elt)
515 (if lis3
516 (progn
517 ;; The following is done because:
518 ;; In merging with ancestor, we don't intersect
519 ;; with lis3. So, it is possible that elt is a
520 ;; file in auxdir1/2 but a directory in auxdir3
521 ;; Or elt may not exist in auxdir3 at all.
522 ;; In the first case, we add a slash at the end.
523 ;; In the second case, we insert nil.
524 (setq elt (ediff-add-slash-if-directory auxdir3 elt))
525 (if (file-exists-p (concat auxdir3 elt))
526 (concat auxdir3 elt))))))
92c51e07 527 common))
fa2eb9ac
MK
528 ))
529
328b4b70
MK
530;; find directory files that are under revision. Include subdirectories, since
531;; we may visit them recursively. DIR1 is the directory to inspect.
532;; MERGE-AUTOSTORE-DIR is the directory where to auto-store the results of
3af0304a 533;; merges. Can be nil.
92c51e07
MK
534(defun ediff-get-directory-files-under-revision (jobname
535 regexp dir1
536 &optional merge-autostore-dir)
fa2eb9ac
MK
537 (let (lis1 elt common auxdir1)
538 (setq auxdir1 (file-name-as-directory dir1)
539 lis1 (directory-files auxdir1 nil regexp))
540
3af0304a 541 (if (ediff-nonempty-string-p merge-autostore-dir)
92c51e07
MK
542 (setq merge-autostore-dir
543 (file-name-as-directory merge-autostore-dir)))
544
fa2eb9ac
MK
545 (while lis1
546 (setq elt (car lis1)
547 lis1 (cdr lis1))
548 ;; take files under revision control
549 (cond ((file-directory-p (concat auxdir1 elt))
328b4b70
MK
550 (setq common
551 (cons (ediff-add-slash-if-directory auxdir1 elt) common)))
3af0304a
MK
552 ((and (featurep 'vc-hooks) (vc-backend (concat auxdir1 elt)))
553 (setq common (cons elt common)))
554 ;; The following two are needed only if vc-hooks isn't loaded.
555 ;; They won't recognize CVS files.
fa2eb9ac 556 ((file-exists-p (concat auxdir1 elt ",v"))
4ae69eac
MK
557 (setq common (cons elt common)))
558 ((file-exists-p (concat auxdir1 "RCS/" elt ",v"))
559 (setq common (cons elt common)))
560 ) ; cond
fa2eb9ac
MK
561 ) ; while
562
328b4b70
MK
563 (setq common (delete "./" common)
564 common (delete "../" common)
3af0304a
MK
565 common (delete "RCS" common)
566 common (delete "CVS" common)
567 )
fa2eb9ac 568
4ae69eac
MK
569 ;; copying is needed because sort sorts via side effects
570 (setq common (sort (ediff-copy-list common) 'string-lessp))
fa2eb9ac
MK
571
572 ;; return result
92c51e07 573 (cons (list regexp auxdir1 nil nil merge-autostore-dir)
3af0304a 574 (mapcar (lambda (elt) (list (concat auxdir1 elt) nil nil))
fa2eb9ac
MK
575 common))
576 ))
577
578
579;; If file groups selected by patterns will ever be implemented, this
580;; comparison function might become useful.
581;;;; uses external variables PAT1 PAT2 to compare str1/2
582;;;; patterns must be of the form ???*???? where ??? are strings of chars
583;;;; containing no *.
584;;(defun ediff-pattern= (str1 str2)
585;; (let (pos11 pos12 pos21 pos22 len1 len2)
586;; (setq pos11 0
587;; len (length epat1)
588;; pos12 len)
589;; (while (and (< pos11 len) (not (= (aref epat1 pos11) ?*)))
590;; (setq pos11 (1+ pos11)))
591;; (while (and (> pos12 0) (not (= (aref epat1 (1- pos12)) ?*)))
592;; (setq pos12 (1- pos12)))
593;;
594;; (setq pos21 0
595;; len (length epat2)
596;; pos22 len)
597;; (while (and (< pos21 len) (not (= (aref epat2 pos21) ?*)))
598;; (setq pos21 (1+ pos21)))
599;; (while (and (> pos22 0) (not (= (aref epat2 (1- pos22)) ?*)))
600;; (setq pos22 (1- pos22)))
601;;
602;; (if (and (> (length str1) pos12) (>= pos12 pos11) (> pos11 -1)
603;; (> (length str2) pos22) (>= pos22 pos21) (> pos21 -1))
604;; (string= (substring str1 pos11 pos12)
605;; (substring str2 pos21 pos22)))
606;; ))
607
608
609;; Prepare meta-buffer in accordance with the argument-function and
3af0304a 610;; redraw-function. Must return the created meta-buffer.
fa2eb9ac
MK
611(defun ediff-prepare-meta-buffer (action-func meta-list
612 meta-buffer-name redraw-function
613 jobname &optional startup-hooks)
614 (let* ((meta-buffer-name
615 (ediff-unique-buffer-name meta-buffer-name "*"))
616 (meta-buffer (get-buffer-create meta-buffer-name)))
e756eb9f 617 (ediff-with-current-buffer meta-buffer
fa2eb9ac
MK
618
619 ;; comes first
620 (ediff-meta-mode)
621
622 (setq ediff-meta-action-function action-func
623 ediff-meta-redraw-function redraw-function
624 ediff-metajob-name jobname
625 ediff-meta-buffer meta-buffer)
626
627 ;; comes after ediff-meta-action-function is set
628 (ediff-setup-meta-map)
629
630 (if (eq ediff-metajob-name 'ediff-registry)
631 (progn
632 (setq ediff-registry-buffer meta-buffer
633 ediff-meta-list meta-list)
634 ;; this func is used only from registry buffer, not from other
635 ;; meta-buffs.
636 (define-key
637 ediff-meta-buffer-map "M" 'ediff-show-meta-buff-from-registry))
92c51e07
MK
638 ;; Initialize the meta list -- don't do this for registry.
639 ;;
640 ;; We prepend '(nil nil) to all elts of meta-list, except the first.
3af0304a 641 ;; The first nil will later be replaced by the session buffer. The
92c51e07
MK
642 ;; second is reserved for session status.
643 ;;
fa2eb9ac 644 ;; (car ediff-meta-list) gets cons'ed with the session group buffer.
92c51e07 645 ;; Also, session objects A/B/C are turned into lists of the form
3af0304a
MK
646 ;; (obj eq-indicator). Eq-indicator is either nil or =. Initialized to
647 ;; nil. If later it is discovered that this file is = to some other
92c51e07 648 ;; file in the same session, eq-indicator is changed to `='.
bbe6126c 649 ;; For now, the eq-indicator is used only for 2 and 3-file jobs.
fa2eb9ac
MK
650 (setq ediff-meta-list
651 (cons (cons meta-buffer (car meta-list))
92c51e07 652 (mapcar
3af0304a
MK
653 (lambda (elt)
654 (cons nil
655 (cons nil
656 ;; convert each obj to (obj nil),
657 ;; where nil is the initial value
658 ;; for eq-indicator -- see above
659 (mapcar (lambda (obj) (list obj nil))
660 elt))))
92c51e07 661 (cdr meta-list)))))
fa2eb9ac
MK
662
663 (or (eq meta-buffer ediff-registry-buffer)
664 (setq ediff-session-registry
665 (cons meta-buffer ediff-session-registry)))
666
667 ;; redraw-function uses ediff-meta-list
668 (funcall redraw-function ediff-meta-list)
669
670 ;; set read-only/non-modified
671 (setq buffer-read-only t)
672 (set-buffer-modified-p nil)
673
674 (run-hooks 'startup-hooks)
92c51e07
MK
675
676 ;; Arrange to show directory contents differences
677 ;; Must be after run startup-hooks, since ediff-dir-difference-list is
fa2eb9ac 678 ;; set inside these hooks
bbe6126c 679 (if (eq action-func 'ediff-filegroup-action)
fa2eb9ac
MK
680 (progn
681 ;; put meta buffer in (car ediff-dir-difference-list)
682 (setq ediff-dir-difference-list
683 (cons (cons meta-buffer (car ediff-dir-difference-list))
684 (cdr ediff-dir-difference-list)))
685
bbe6126c 686 (or (ediff-one-filegroup-metajob jobname)
fa2eb9ac 687 (ediff-draw-dir-diffs ediff-dir-difference-list))
3ec8facd
KH
688 (define-key
689 ediff-meta-buffer-map "h" 'ediff-mark-for-hiding-at-pos)
690 (define-key ediff-meta-buffer-map "x" 'ediff-hide-marked-sessions)
691 (define-key
692 ediff-meta-buffer-map "m" 'ediff-mark-for-operation-at-pos)
693 (define-key ediff-meta-buffer-map "u" nil)
fa2eb9ac 694 (define-key
3ec8facd
KH
695 ediff-meta-buffer-map "um" 'ediff-unmark-all-for-operation)
696 (define-key
697 ediff-meta-buffer-map "uh" 'ediff-unmark-all-for-hiding)
bbe6126c
MK
698 (cond ((ediff-collect-diffs-metajob jobname)
699 (define-key
700 ediff-meta-buffer-map "P" 'ediff-collect-custom-diffs))
701 ((ediff-patch-metajob jobname)
702 (define-key
703 ediff-meta-buffer-map "P" 'ediff-meta-show-patch)))
3ec8facd 704 (define-key ediff-meta-buffer-map "^" 'ediff-up-meta-hierarchy)
fa2eb9ac
MK
705 (define-key ediff-meta-buffer-map "D" 'ediff-show-dir-diffs)))
706
707 (if (eq ediff-metajob-name 'ediff-registry)
708 (run-hooks 'ediff-registry-setup-hook)
709 (run-hooks 'ediff-session-group-setup-hook))
710 ) ; eval in meta-buffer
711 meta-buffer))
712
328b4b70
MK
713;; Insert the activity marker for session SESSION in the meta buffer at point
714;; The activity marker is either SPC (untouched session), or + (active session,
715;; i.e., ediff is currently run in it), or - (finished session, i.e., we've ran
716;; ediff in it and then exited)
717(defun ediff-insert-session-activity-marker-in-meta-buffer (session)
718 (insert
719 (cond ((ediff-get-session-activity-marker session))
720 ;; virgin session
721 (t " "))))
722
3af0304a 723;; Insert session status at point. Status is either ?H (marked for hiding), or
328b4b70
MK
724;; ?I (hidden or invalid), or ?* (meaning marked for an operation; currently,
725;; such op can only be checking for equality)), or SPC (meaning neither marked
726;; nor invalid)
727(defun ediff-insert-session-status-in-meta-buffer (session)
728 (insert
729 (cond ((ediff-get-session-status session)) ; session has status: ?H, ?I, ?*
730 ;; normal session, no marks or hidings
731 (t " "))))
732
733;; If NEW-MARKER is non-nil, use it to substitute the current activity marker
3af0304a 734;; in the meta buffer. If nil, use SPC
328b4b70
MK
735(defun ediff-replace-session-activity-marker-in-meta-buffer (point new-marker)
736 (let* ((overl (ediff-get-meta-overlay-at-pos point))
737 (session-info (ediff-overlay-get overl 'ediff-meta-info))
738 (activity-marker (ediff-get-session-activity-marker session-info))
739 buffer-read-only)
740 (or new-marker activity-marker (setq new-marker ?\ ))
741 (goto-char (ediff-overlay-start overl))
742 (if (eq (char-after (point)) new-marker)
743 () ; if marker shown in buffer is the same as new-marker, do nothing
744 (insert new-marker)
745 (delete-char 1)
746 (set-buffer-modified-p nil))))
747
748;; If NEW-STATUS is non-nil, use it to substitute the current status marker in
3af0304a 749;; the meta buffer. If nil, use SPC
328b4b70
MK
750(defun ediff-replace-session-status-in-meta-buffer (point new-status)
751 (let* ((overl (ediff-get-meta-overlay-at-pos point))
752 (session-info (ediff-overlay-get overl 'ediff-meta-info))
753 (status (ediff-get-session-status session-info))
754 buffer-read-only)
755 (setq new-status (or new-status status ?\ ))
756 (goto-char (ediff-overlay-start overl))
757 (forward-char 1) ; status is the second char in session record
758 (if (eq (char-after (point)) new-status)
759 () ; if marker shown in buffer is the same as new-marker, do nothing
760 (insert new-status)
761 (delete-char 1)
762 (set-buffer-modified-p nil))))
763
764;; insert all file info in meta buffer for a given session
765(defun ediff-insert-session-info-in-meta-buffer (session-info sessionNum)
766 (let ((f1 (ediff-get-session-objA session-info))
767 (f2 (ediff-get-session-objB session-info))
768 (f3 (ediff-get-session-objC session-info))
769 (pt (point))
770 (hidden (eq (ediff-get-session-status session-info) ?I)))
771 ;; insert activity marker, i.e., SPC, - or +
772 (ediff-insert-session-activity-marker-in-meta-buffer session-info)
773 ;; insert session status, i.e., *, H
774 (ediff-insert-session-status-in-meta-buffer session-info)
775 (insert " Session " (int-to-string sessionNum) ":\n")
776 (ediff-meta-insert-file-info1 f1)
777 (ediff-meta-insert-file-info1 f2)
778 (ediff-meta-insert-file-info1 f3)
779 (ediff-set-meta-overlay pt (point) session-info sessionNum hidden)))
780
fa2eb9ac
MK
781
782;; this is a setup function for ediff-directories
783;; must return meta-buffer
784(defun ediff-redraw-directory-group-buffer (meta-list)
785 ;; extract directories
786 (let ((meta-buf (ediff-get-group-buffer meta-list))
787 (empty t)
788 (sessionNum 0)
328b4b70 789 regexp elt merge-autostore-dir
fa2eb9ac 790 point tmp-list buffer-read-only)
e756eb9f 791 (ediff-with-current-buffer meta-buf
fa2eb9ac
MK
792 (setq point (point))
793 (erase-buffer)
328b4b70
MK
794 ;; delete phony overlays that used to represent sessions before the buff
795 ;; was redrawn
796 (if ediff-emacs-p
797 (mapcar 'delete-overlay (overlays-in 1 1))
798 (map-extents 'delete-extent))
799
fa2eb9ac
MK
800 (insert (format ediff-meta-buffer-message
801 (ediff-abbrev-jobname ediff-metajob-name)))
802
92c51e07 803 (setq regexp (ediff-get-group-regexp meta-list)
328b4b70
MK
804 merge-autostore-dir
805 (ediff-get-group-merge-autostore-dir meta-list))
fa2eb9ac 806
bbe6126c
MK
807 (cond ((ediff-collect-diffs-metajob)
808 (insert
328b4b70 809 " P:\tcollect custom diffs of all marked sessions\n"))
bbe6126c
MK
810 ((ediff-patch-metajob)
811 (insert
328b4b70 812 " P:\tshow patch appropriately for the context (session or group)\n")))
fa2eb9ac 813 (insert
3ec8facd 814 " ^:\tshow parent session group\n")
bbe6126c
MK
815 (or (ediff-one-filegroup-metajob)
816 (insert
328b4b70 817 " D:\tshow differences among directories\n"
3ec8facd
KH
818 " ==:\tfor each session, show which files are identical\n"
819 " =h:\tlike ==, but also marks those sessions for hiding\n"
820 " =m:\tlike ==, but also marks those sessions for operation\n\n"))
fa2eb9ac 821
328b4b70 822 (insert "\n")
fa2eb9ac 823 (if (and (stringp regexp) (> (length regexp) 0))
ddc90f39 824 (insert
328b4b70
MK
825 (format "*** Filter-through regular expression: %s\n" regexp)))
826 (ediff-insert-dirs-in-meta-buffer meta-list)
92c51e07 827 (if (and ediff-autostore-merges (ediff-merge-metajob)
3af0304a 828 (ediff-nonempty-string-p merge-autostore-dir))
92c51e07 829 (insert (format
328b4b70 830 "\nMerge results are automatically stored in:\n\t%s\n"
92c51e07 831 merge-autostore-dir)))
fa2eb9ac 832 (insert "\n
bbe6126c 833 Size Last modified Name
328b4b70 834 ----------------------------------------------
fa2eb9ac
MK
835
836")
837
838 ;; discard info on directories and regexp
839 (setq meta-list (cdr meta-list)
840 tmp-list meta-list)
841 (while (and tmp-list empty)
842 (if (and (car tmp-list)
843 (not (eq (ediff-get-session-status (car tmp-list)) ?I)))
844 (setq empty nil))
845 (setq tmp-list (cdr tmp-list)))
846
847 (if empty
848 (insert
849 " ****** ****** This session group has no members\n"))
850
851 ;; now organize file names like this:
fa2eb9ac 852 ;; use-mark sizeA dateA sizeB dateB filename
fa2eb9ac 853 ;; make sure directories are displayed with a trailing slash.
fa2eb9ac
MK
854 (while meta-list
855 (setq elt (car meta-list)
856 meta-list (cdr meta-list)
857 sessionNum (1+ sessionNum))
858 (if (eq (ediff-get-session-status elt) ?I)
859 ()
328b4b70 860 (ediff-insert-session-info-in-meta-buffer elt sessionNum)))
fa2eb9ac
MK
861 (set-buffer-modified-p nil)
862 (goto-char point)
863 meta-buf)))
864
328b4b70
MK
865(defun ediff-update-markers-in-dir-meta-buffer (meta-list)
866 (let ((meta-buf (ediff-get-group-buffer meta-list))
867 session-info point overl buffer-read-only)
868 (ediff-with-current-buffer meta-buf
869 (setq point (point))
870 (goto-char (point-min))
871 (ediff-next-meta-item1)
872 (while (not (bobp))
873 (setq session-info (ediff-get-meta-info meta-buf (point) 'no-error)
874 overl (ediff-get-meta-overlay-at-pos (point)))
875 (if session-info
876 (progn
877 (cond ((eq (ediff-get-session-status session-info) ?I)
878 ;; Do hiding
879 (if overl (ediff-overlay-put overl 'invisible t)))
880 ((and (eq (ediff-get-session-status session-info) ?H)
881 overl (ediff-overlay-get overl 'invisible))
882 ;; Do unhiding
883 (ediff-overlay-put overl 'invisible nil))
884 (t (ediff-replace-session-activity-marker-in-meta-buffer
885 (point)
886 (ediff-get-session-activity-marker session-info))
887 (ediff-replace-session-status-in-meta-buffer
888 (point)
889 (ediff-get-session-status session-info))))))
890 (ediff-next-meta-item1) ; advance to the next item
891 ) ; end while
892 (set-buffer-modified-p nil)
893 (goto-char point))
894 meta-buf))
895
896(defun ediff-update-session-marker-in-dir-meta-buffer (session-num)
897 (let (buffer-meta-overlays session-info overl buffer-read-only)
898 (setq overl
899 (if ediff-xemacs-p
900 (map-extents
3af0304a
MK
901 (lambda (ext maparg)
902 (if (and
903 (ediff-overlay-get ext 'ediff-meta-info)
904 (eq (ediff-overlay-get ext 'ediff-meta-session-number)
905 session-num))
906 ext)))
328b4b70
MK
907 ;; Emacs doesn't have map-extents, so try harder
908 ;; Splice overlay lists to get all buffer overlays
909 (setq buffer-meta-overlays (overlay-lists)
910 buffer-meta-overlays (append (car buffer-meta-overlays)
911 (cdr buffer-meta-overlays)))
912 (car
913 (delq nil
914 (mapcar
3af0304a
MK
915 (lambda (overl)
916 (if (and
917 (ediff-overlay-get overl 'ediff-meta-info)
918 (eq (ediff-overlay-get
919 overl 'ediff-meta-session-number)
920 session-num))
921 overl))
328b4b70
MK
922 buffer-meta-overlays)))))
923 (or overl
924 (error
925 "Bug in ediff-update-session-marker-in-dir-meta-buffer: no overlay with given number %S"
926 session-num))
927 (setq session-info (ediff-overlay-get overl 'ediff-meta-info))
928 (goto-char (ediff-overlay-start overl))
929 (ediff-replace-session-activity-marker-in-meta-buffer
930 (point)
931 (ediff-get-session-activity-marker session-info))
932 (ediff-replace-session-status-in-meta-buffer
933 (point)
934 (ediff-get-session-status session-info)))
935 (ediff-next-meta-item1))
936
937
938
fa2eb9ac 939;; Check if this is a problematic session.
3af0304a 940;; Return nil if not. Otherwise, return symbol representing the problem
fa2eb9ac 941;; At present, problematic sessions occur only in -with-ancestor comparisons
92c51e07
MK
942;; when the ancestor is a directory rather than a file, or when there is no
943;; suitable ancestor file in the ancestor directory
fa2eb9ac 944(defun ediff-problematic-session-p (session)
bbe6126c
MK
945 (let ((f1 (ediff-get-session-objA-name session))
946 (f2 (ediff-get-session-objB-name session))
947 (f3 (ediff-get-session-objC-name session)))
fa2eb9ac
MK
948 (cond ((and (stringp f1) (not (file-directory-p f1))
949 (stringp f2) (not (file-directory-p f2))
92c51e07
MK
950 ;; either invalid file name or a directory
951 (or (not (stringp f3)) (file-directory-p f3))
fa2eb9ac
MK
952 (ediff-ancestor-metajob))
953 ;; more may be added later
954 'ancestor-is-dir)
955 (t nil))))
956
328b4b70 957(defun ediff-meta-insert-file-info1 (fileinfo)
92c51e07 958 (let ((fname (car fileinfo))
bbe6126c 959 (feq (ediff-get-file-eqstatus fileinfo))
328b4b70
MK
960 (max-filename-width (if ediff-meta-truncate-filenames
961 (- (window-width) 41)
962 500))
92c51e07 963 file-modtime file-size)
92c51e07
MK
964 (cond ((not (stringp fname)) (setq file-size -2)) ; file doesn't exits
965 ((not (ediff-file-remote-p fname))
966 (if (file-exists-p fname)
967 ;; set real size and modtime
968 (setq file-size (ediff-file-size fname)
969 file-modtime (ediff-file-modtime fname))
970 (setq file-size -2))) ; file doesn't exist
971 ( t (setq file-size -1))) ; remote file
bbe6126c
MK
972 (if (stringp fname)
973 (insert
974 (format
975 "%s %s %-20s %s\n"
976 (if feq "=" " ") ; equality indicator
92c51e07
MK
977 (format "%10s" (cond ((= file-size -1) "--")
978 ((< file-size -1) "--")
979 (t file-size)))
980 (cond ((= file-size -1) "*remote file*")
981 ((< file-size -1) "*file doesn't exist*")
982 (t (ediff-format-date (decode-time file-modtime))))
983
984 ;; dir names in meta lists have training slashes, so we just
985 ;; abbreviate the file name, if file exists
986 (if (and (not (stringp fname)) (< file-size -1))
987 "-------" ; file doesn't exist
328b4b70
MK
988 (ediff-truncate-string-left
989 (ediff-abbreviate-file-name fname)
990 max-filename-width)))))))
fa2eb9ac 991
bbe6126c
MK
992(defconst ediff-months '((1 . "Jan") (2 . "Feb") (3 . "Mar") (4 . "Apr")
993 (5 . "May") (6 . "Jun") (7 . "Jul") (8 . "Aug")
994 (9 . "Sep") (10 . "Oct") (11 . "Nov") (12 . "Dec"))
995 "Months' associative array.")
996
92c51e07
MK
997;; returns 2char string
998(defsubst ediff-fill-leading-zero (num)
999 (if (< num 10)
1000 (format "0%d" num)
1001 (number-to-string num)))
1002
bbe6126c
MK
1003;; TIME is like the output of decode-time
1004(defun ediff-format-date (time)
1005 (format "%s %2d %4d %s:%s:%s"
1006 (cdr (assoc (nth 4 time) ediff-months)) ; month
1007 (nth 3 time) ; day
1008 (nth 5 time) ; year
1009 (ediff-fill-leading-zero (nth 2 time)) ; hour
1010 (ediff-fill-leading-zero (nth 1 time)) ; min
1011 (ediff-fill-leading-zero (nth 0 time)) ; sec
1012 ))
1013
328b4b70
MK
1014;; Draw the directories
1015(defun ediff-insert-dirs-in-meta-buffer (meta-list)
1016 (let* ((dir1 (ediff-abbreviate-file-name (ediff-get-group-objA meta-list)))
1017 (dir2 (ediff-get-group-objB meta-list))
1018 (dir2 (if (stringp dir2) (ediff-abbreviate-file-name dir2)))
1019 (dir3 (ediff-get-group-objC meta-list))
1020 (dir3 (if (stringp dir3) (ediff-abbreviate-file-name dir3))))
1021 (insert "*** Directory A: " dir1 "\n")
1022 (if dir2 (insert "*** Directory B: " dir2 "\n"))
1023 (if dir3 (insert "*** Directory C: " dir3 "\n"))
1024 (insert "\n")))
1025
fa2eb9ac
MK
1026(defun ediff-draw-dir-diffs (diff-list)
1027 (if (null diff-list) (error "Lost difference info on these directories"))
1028 (let* ((buf-name (ediff-unique-buffer-name
1029 "*Ediff File Group Differences" "*"))
1030 (regexp (ediff-get-group-regexp diff-list))
1031 (dir1 (ediff-abbreviate-file-name (ediff-get-group-objA diff-list)))
1032 (dir2 (ediff-abbreviate-file-name (ediff-get-group-objB diff-list)))
1033 (dir3 (ediff-get-group-objC diff-list))
1034 (dir3 (if (stringp dir3) (ediff-abbreviate-file-name dir3)))
1035 (meta-buf (ediff-get-group-buffer diff-list))
1036 (underline (make-string 26 ?-))
1037 file code
1038 buffer-read-only)
1039 ;; skip the directory part
1040 (setq diff-list (cdr diff-list))
1041 (setq ediff-dir-diffs-buffer (get-buffer-create buf-name))
e756eb9f 1042 (ediff-with-current-buffer ediff-dir-diffs-buffer
fa2eb9ac
MK
1043 (use-local-map ediff-dir-diffs-buffer-map)
1044 (erase-buffer)
1045 (setq ediff-meta-buffer meta-buf)
1046 (insert "\t\t*** Directory Differences ***\n")
1047 (insert "
1048Useful commands:
1049 `q': hide this buffer
328b4b70
MK
1050 n,SPC: next line
1051 p,DEL: previous line\n\n")
fa2eb9ac
MK
1052
1053 (if (and (stringp regexp) (> (length regexp) 0))
ddc90f39
MK
1054 (insert
1055 (format "\n*** Filter-through regular expression: %s\n" regexp)))
fa2eb9ac
MK
1056 (insert "\n")
1057 (insert (format "\n%-27s%-26s"
1058 (ediff-truncate-string-left
1059 (ediff-abbreviate-file-name
1060 (file-name-as-directory dir1))
1061 25)
1062 (ediff-truncate-string-left
1063 (ediff-abbreviate-file-name
1064 (file-name-as-directory dir2))
1065 25)))
1066 (if dir3
1067 (insert (format " %-25s\n"
1068 (ediff-truncate-string-left
1069 (ediff-abbreviate-file-name
1070 (file-name-as-directory dir3))
1071 25)))
1072 (insert "\n"))
1073 (insert (format "%s%s" underline underline))
1074 (if (stringp dir3)
1075 (insert (format "%s\n\n" underline))
1076 (insert "\n\n"))
1077
1078 (if (null diff-list)
1079 (insert "\n\t*** No differences ***\n"))
1080
1081 (while diff-list
1082 (setq file (car (car diff-list))
1083 code (cdr (car diff-list))
1084 diff-list (cdr diff-list))
1085 (if (= (mod code 2) 0) ; dir1
1086 (insert (format "%-27s"
1087 (ediff-truncate-string-left
1088 (ediff-abbreviate-file-name
1089 (if (file-directory-p (concat dir1 file))
1090 (file-name-as-directory file)
1091 file))
1092 24)))
1093 (insert (format "%-27s" "---")))
1094 (if (= (mod code 3) 0) ; dir2
1095 (insert (format "%-26s"
1096 (ediff-truncate-string-left
1097 (ediff-abbreviate-file-name
1098 (if (file-directory-p (concat dir2 file))
1099 (file-name-as-directory file)
1100 file))
1101 24)))
1102 (insert (format "%-26s" "---")))
1103 (if (stringp dir3)
1104 (if (= (mod code 5) 0) ; dir3
1105 (insert (format " %-25s"
1106 (ediff-truncate-string-left
1107 (ediff-abbreviate-file-name
1108 (if (file-directory-p (concat dir3 file))
1109 (file-name-as-directory file)
1110 file))
1111 24)))
1112 (insert (format " %-25s" "---"))))
1113 (insert "\n"))
1114 (setq buffer-read-only t)
1115 (set-buffer-modified-p nil)) ; eval in diff buffer
1116 ))
1117
1118(defun ediff-bury-dir-diffs-buffer ()
3af0304a 1119 "Bury the directory difference buffer. Display the meta buffer instead."
fa2eb9ac
MK
1120 (interactive)
1121 (let ((buf ediff-meta-buffer)
1122 wind)
1123 (bury-buffer)
1124 (if (setq wind (ediff-get-visible-buffer-window buf))
1125 (select-window wind)
1126 (set-window-buffer (selected-window) buf))))
1127
1128;; executes in dir session group buffer
1129;; show buffer differences
1130(defun ediff-show-dir-diffs ()
1131 "Display differences among the directories involved in session group."
1132 (interactive)
bbe6126c 1133 (if (ediff-one-filegroup-metajob)
fa2eb9ac
MK
1134 (error "This command is inapplicable in the present context"))
1135 (or (ediff-buffer-live-p ediff-dir-diffs-buffer)
1136 (ediff-draw-dir-diffs ediff-dir-difference-list))
1137 (let ((buf ediff-dir-diffs-buffer))
1138 (other-window 1)
1139 (set-window-buffer (selected-window) buf)
1140 (goto-char (point-min))))
1141
1142(defun ediff-up-meta-hierarchy ()
1143 "Go to the parent session group buffer."
1144 (interactive)
1145 (if (ediff-buffer-live-p ediff-parent-meta-buffer)
328b4b70
MK
1146 (ediff-show-meta-buffer
1147 ediff-parent-meta-buffer ediff-meta-session-number)
fa2eb9ac
MK
1148 (error "This session group has no parent")))
1149
1150
1151;; argument is ignored
1152(defun ediff-redraw-registry-buffer (&optional ignore)
e756eb9f 1153 (ediff-with-current-buffer ediff-registry-buffer
fa2eb9ac
MK
1154 (let ((point (point))
1155 elt bufAname bufBname bufCname cur-diff total-diffs pt
1156 job-name meta-list registry-list buffer-read-only)
1157 (erase-buffer)
328b4b70
MK
1158 ;; delete phony overlays that used to represent sessions before the buff
1159 ;; was redrawn
1160 (if ediff-emacs-p
1161 (mapcar 'delete-overlay (overlays-in 1 1))
1162 (map-extents 'delete-extent))
1163
fa2eb9ac
MK
1164 (insert "This is a registry of all active Ediff sessions.
1165
1166Useful commands:
1167 button2, `v', RET over a session record: switch to that session
328b4b70
MK
1168 M over a session record: display the associated session group
1169 R in any Ediff session: display session registry
1170 n,SPC: next session
1171 p,DEL: previous session
1172 E: browse Ediff on-line manual
1173 q: bury registry
fa2eb9ac
MK
1174
1175
1176\t\tActive Ediff Sessions:
1177\t\t----------------------
1178
1179")
1180 ;; purge registry list from dead buffers
3af0304a
MK
1181 (mapcar (lambda (elt)
1182 (if (not (ediff-buffer-live-p elt))
1183 (setq ediff-session-registry
1184 (delq elt ediff-session-registry))))
fa2eb9ac
MK
1185 ediff-session-registry)
1186
1187 (if (null ediff-session-registry)
1188 (insert " ******* No active Ediff sessions *******\n"))
1189
1190 (setq registry-list ediff-session-registry)
1191 (while registry-list
1192 (setq elt (car registry-list)
1193 registry-list (cdr registry-list))
1194
1195 (if (ediff-buffer-live-p elt)
e756eb9f 1196 (if (ediff-with-current-buffer elt
fa2eb9ac
MK
1197 (setq job-name ediff-metajob-name
1198 meta-list ediff-meta-list)
1199 (and ediff-metajob-name
1200 (not (eq ediff-metajob-name 'ediff-registry))))
1201 (progn
1202 (setq pt (point))
1203 (insert (format " *group*\t%s: %s\n"
1204 (buffer-name elt)
1205 (ediff-abbrev-jobname job-name)))
1206 (insert (format "\t\t %s %s %s\n"
1207 (ediff-abbreviate-file-name
1208 (ediff-get-group-objA meta-list))
1209 (ediff-abbreviate-file-name
bbe6126c
MK
1210 (if (stringp
1211 (ediff-get-group-objB meta-list))
1212 (ediff-get-group-objB meta-list)
1213 ""))
fa2eb9ac 1214 (ediff-abbreviate-file-name
bbe6126c
MK
1215 (if (stringp
1216 (ediff-get-group-objC meta-list))
1217 (ediff-get-group-objC meta-list)
1218 ""))))
fa2eb9ac
MK
1219 (ediff-set-meta-overlay pt (point) elt))
1220 (progn
e756eb9f 1221 (ediff-with-current-buffer elt
fa2eb9ac
MK
1222 (setq bufAname (if (ediff-buffer-live-p ediff-buffer-A)
1223 (buffer-name ediff-buffer-A)
1224 "!!!killed buffer!!!")
1225 bufBname (if (ediff-buffer-live-p ediff-buffer-B)
1226 (buffer-name ediff-buffer-B)
1227 "!!!killed buffer!!!")
1228 bufCname (cond ((not (ediff-3way-job))
1229 "")
1230 ((ediff-buffer-live-p ediff-buffer-C)
1231 (buffer-name ediff-buffer-C))
1232 (t "!!!killed buffer!!!")))
1233 (setq total-diffs (format "%-4d" ediff-number-of-differences)
1234 cur-diff
1235 (cond ((= ediff-current-difference -1) " _")
1236 ((= ediff-current-difference
1237 ediff-number-of-differences)
1238 " $")
1239 (t (format
1240 "%4d" (1+ ediff-current-difference))))
1241 job-name ediff-job-name))
1242 ;; back in the meta buf
1243 (setq pt (point))
1244 (insert cur-diff "/" total-diffs "\t"
1245 (buffer-name elt)
1246 (format ": %s" (ediff-abbrev-jobname job-name)))
1247 (insert
1248 "\n\t\t " bufAname " " bufBname " " bufCname "\n")
1249 (ediff-set-meta-overlay pt (point) elt))))
1250 ) ; while
1251 (set-buffer-modified-p nil)
1252 (goto-char point)
1253 )))
1254
328b4b70
MK
1255;; Sets overlay around a meta record with 'ediff-meta-info property PROP
1256;; If optional SESSION-NUMBER, make it a property of the overlay,
1257;; ediff-meta-session-number
1258(defun ediff-set-meta-overlay (b e prop &optional session-number hidden)
fa2eb9ac
MK
1259 (let (overl)
1260 (setq overl (ediff-make-overlay b e))
1261 (if ediff-emacs-p
1262 (ediff-overlay-put overl 'mouse-face 'highlight)
1263 (ediff-overlay-put overl 'highlight t))
328b4b70
MK
1264 (ediff-overlay-put overl 'ediff-meta-info prop)
1265 (ediff-overlay-put overl 'invisible hidden)
1266 (if (numberp session-number)
1267 (ediff-overlay-put overl 'ediff-meta-session-number session-number))))
fa2eb9ac 1268
3ec8facd 1269(defun ediff-mark-for-hiding-at-pos (unmark)
3af0304a 1270 "Mark session for hiding. With prefix arg, unmark."
fa2eb9ac
MK
1271 (interactive "P")
1272 (let* ((pos (ediff-event-point last-command-event))
1273 (meta-buf (ediff-event-buffer last-command-event))
1274 ;; ediff-get-meta-info gives error if meta-buf or pos are invalid
1275 (info (ediff-get-meta-info meta-buf pos))
328b4b70 1276 (session-number (ediff-get-session-number-at-pos pos)))
3ec8facd
KH
1277 (ediff-mark-session-for-hiding info unmark)
1278 (ediff-next-meta-item 1)
1279 (save-excursion
1280 (ediff-update-meta-buffer meta-buf nil session-number))
fa2eb9ac
MK
1281 ))
1282
3ec8facd
KH
1283;; Returns whether session was marked or unmarked
1284(defun ediff-mark-session-for-hiding (info unmark)
1285 (let ((session-buf (ediff-get-session-buffer info))
1286 ignore)
1287 (cond ((eq unmark 'mark) (setq unmark nil))
1288 ((eq (ediff-get-session-status info) ?H) (setq unmark t))
1289 (unmark ; says unmark, but the marker is different from H
1290 (setq ignore t)))
1291 (cond (ignore)
1292 (unmark (ediff-set-session-status info nil))
1293;;; (if (ediff-buffer-live-p session-buf)
1294;;; (error "Can't hide active session, %s" (buffer-name session-buf)))
1295 (t (ediff-set-session-status info ?H))))
1296 unmark)
1297
1298
1299(defun ediff-mark-for-operation-at-pos (unmark)
3af0304a 1300 "Mark session for a group operation. With prefix arg, unmark."
fa2eb9ac
MK
1301 (interactive "P")
1302 (let* ((pos (ediff-event-point last-command-event))
1303 (meta-buf (ediff-event-buffer last-command-event))
1304 ;; ediff-get-meta-info gives error if meta-buf or pos are invalid
328b4b70
MK
1305 (info (ediff-get-meta-info meta-buf pos))
1306 (session-number (ediff-get-session-number-at-pos pos)))
3ec8facd
KH
1307 (ediff-mark-session-for-operation info unmark)
1308 (ediff-next-meta-item 1)
1309 (save-excursion
1310 (ediff-update-meta-buffer meta-buf nil session-number))
fa2eb9ac
MK
1311 ))
1312
3ec8facd
KH
1313
1314;; returns whether session was unmarked.
1315;; remember: this is a toggle op
1316(defun ediff-mark-session-for-operation (info unmark)
1317 (let (ignore)
1318 (cond ((eq unmark 'mark) (setq unmark nil))
1319 ((eq (ediff-get-session-status info) ?*) (setq unmark t))
1320 (unmark ; says unmark, but the marker is different from *
1321 (setq ignore t)))
1322 (cond (ignore)
1323 (unmark (ediff-set-session-status info nil))
1324 (t (ediff-set-session-status info ?*))))
1325 unmark)
1326
1327
fa2eb9ac 1328(defun ediff-hide-marked-sessions (unhide)
3af0304a 1329 "Hide marked sessions. With prefix arg, unhide."
fa2eb9ac
MK
1330 (interactive "P")
1331 (let ((grp-buf (ediff-get-group-buffer ediff-meta-list))
1332 (meta-list (cdr ediff-meta-list))
1333 (from (if unhide ?I ?H))
1334 (to (if unhide ?H ?I))
1335 (numMarked 0)
bbe6126c 1336 active-sessions-exist session-buf elt)
fa2eb9ac
MK
1337 (while meta-list
1338 (setq elt (car meta-list)
bbe6126c
MK
1339 meta-list (cdr meta-list)
1340 session-buf (ediff-get-session-buffer elt))
1341
fa2eb9ac
MK
1342 (if (eq (ediff-get-session-status elt) from)
1343 (progn
1344 (setq numMarked (1+ numMarked))
bbe6126c
MK
1345 (if (and (eq to ?I) (buffer-live-p session-buf))
1346 ;; shouldn't hide active sessions
1347 (setq active-sessions-exist t)
1348 (ediff-set-session-status elt to)))))
fa2eb9ac 1349 (if (> numMarked 0)
3af0304a 1350 (ediff-update-meta-buffer grp-buf 'must-redraw)
fa2eb9ac
MK
1351 (beep)
1352 (if unhide
1353 (message "Nothing to reveal...")
1354 (message "Nothing to hide...")))
bbe6126c 1355 (if active-sessions-exist
328b4b70 1356 (message "Note: Ediff didn't hide active sessions!"))
fa2eb9ac
MK
1357 ))
1358
3af0304a 1359;; Apply OPERATION to marked sessions. Operation expects one argument of type
fa2eb9ac
MK
1360;; meta-list member (not the first one), i.e., a regular session description.
1361;; Returns number of marked sessions on which operation was performed
1362(defun ediff-operate-on-marked-sessions (operation)
1363 (let ((grp-buf (ediff-get-group-buffer ediff-meta-list))
1364 (meta-list (cdr ediff-meta-list))
1365 (marksym ?*)
1366 (numMarked 0)
1367 (sessionNum 0)
bd698e98
MK
1368 (diff-buffer ediff-meta-diff-buffer)
1369 session-buf elt)
fa2eb9ac
MK
1370 (while meta-list
1371 (setq elt (car meta-list)
1372 meta-list (cdr meta-list)
1373 sessionNum (1+ sessionNum))
bd698e98
MK
1374 (cond ((eq (ediff-get-session-status elt) marksym)
1375 (save-excursion
1376 (setq numMarked (1+ numMarked))
1377 (funcall operation elt sessionNum)))
1378 ((and (ediff-meta-session-p elt)
1379 (ediff-buffer-live-p
1380 (setq session-buf (ediff-get-session-buffer elt))))
1381 (setq numMarked
1382 (+ numMarked
1383 (ediff-with-current-buffer session-buf
1384 ;; pass meta-diff along
1385 (setq ediff-meta-diff-buffer diff-buffer)
1386 ;; collect diffs in child group
1387 (ediff-operate-on-marked-sessions operation)))))))
328b4b70 1388 (ediff-update-meta-buffer grp-buf 'must-redraw) ; just in case
fa2eb9ac
MK
1389 numMarked
1390 ))
1391
1392(defun ediff-append-custom-diff (session sessionNum)
1393 (or (ediff-collect-diffs-metajob)
bd698e98 1394 (error "Hmm, I'd hate to do it to you ..."))
fa2eb9ac
MK
1395 (let ((session-buf (ediff-get-session-buffer session))
1396 (meta-diff-buff ediff-meta-diff-buffer)
1397 (metajob ediff-metajob-name)
1398 tmp-buf custom-diff-buf)
1399 (if (ediff-buffer-live-p session-buf)
e756eb9f 1400 (ediff-with-current-buffer session-buf
fa2eb9ac 1401 (if (eq ediff-control-buffer session-buf) ; individual session
4ae69eac
MK
1402 (progn
1403 (ediff-compute-custom-diffs-maybe)
1404 (setq custom-diff-buf ediff-custom-diff-buffer)))))
fa2eb9ac
MK
1405
1406 (or (ediff-buffer-live-p meta-diff-buff)
1407 (error "Ediff: something wrong--no multiple diffs buffer"))
1408
1409 (cond ((ediff-buffer-live-p custom-diff-buf)
1410 (save-excursion
1411 (set-buffer meta-diff-buff)
1412 (goto-char (point-max))
1413 (insert-buffer custom-diff-buf)
1414 (insert "\n")))
4ae69eac
MK
1415 ((memq metajob '(ediff-directories
1416 ediff-merge-directories
1417 ediff-merge-directories-with-ancestor))
fa2eb9ac
MK
1418 ;; get diffs by calling shell command on ediff-custom-diff-program
1419 (save-excursion
1420 (set-buffer (setq tmp-buf (get-buffer-create ediff-tmp-buffer)))
1421 (erase-buffer)
1422 (shell-command
1423 (format "%s %s %s %s"
1424 ediff-custom-diff-program ediff-custom-diff-options
bbe6126c
MK
1425 (ediff-get-session-objA-name session)
1426 (ediff-get-session-objB-name session))
fa2eb9ac
MK
1427 t))
1428 (save-excursion
1429 (set-buffer meta-diff-buff)
1430 (goto-char (point-max))
1431 (insert-buffer tmp-buf)
1432 (insert "\n")))
1433 (t
4ae69eac
MK
1434 (error "Can't make context diff for Session %d" sessionNum )))
1435 ))
fa2eb9ac
MK
1436
1437(defun ediff-collect-custom-diffs ()
1438 "Collect custom diffs of marked sessions in buffer `*Ediff Multifile Diffs*'.
1439This operation is defined only for `ediff-directories' and
1440`ediff-directory-revisions', since its intent is to produce
3af0304a 1441multifile patches. For `ediff-directory-revisions', we insist that
fa2eb9ac
MK
1442all marked sessions must be active."
1443 (interactive)
1444 (or (ediff-buffer-live-p ediff-meta-diff-buffer)
1445 (setq ediff-meta-diff-buffer
1446 (get-buffer-create
1447 (ediff-unique-buffer-name "*Ediff Multifile Diffs" "*"))))
e756eb9f 1448 (ediff-with-current-buffer ediff-meta-diff-buffer
fa2eb9ac
MK
1449 (erase-buffer))
1450 (if (> (ediff-operate-on-marked-sessions 'ediff-append-custom-diff) 0)
1451 ;; did something
3af0304a
MK
1452 (progn
1453 (display-buffer ediff-meta-diff-buffer 'not-this-window)
1454 (ediff-with-current-buffer ediff-meta-diff-buffer
1455 (set-buffer-modified-p nil)
1456 (setq buffer-read-only t)))
fa2eb9ac
MK
1457 (beep)
1458 (message "No marked sessions found")))
1459
bbe6126c
MK
1460(defun ediff-meta-show-patch ()
1461 "Show the multi-file patch associated with this group session."
1462 (interactive)
1463 (let* ((pos (ediff-event-point last-command-event))
1464 (meta-buf (ediff-event-buffer last-command-event))
1465 (info (ediff-get-meta-info meta-buf pos 'noerror))
1466 (patchbuffer ediff-meta-patchbufer))
1467 (if (ediff-buffer-live-p patchbuffer)
e756eb9f 1468 (ediff-with-current-buffer patchbuffer
bbe6126c
MK
1469 (save-restriction
1470 (if (not info)
1471 (widen)
1472 (narrow-to-region
1473 (ediff-get-session-objB-name info)
1474 (ediff-get-session-objC-name info)))
1475 (set-buffer (get-buffer-create ediff-tmp-buffer))
1476 (erase-buffer)
1477 (insert-buffer patchbuffer)
1478 (display-buffer ediff-tmp-buffer 'not-this-window)
1479 ))
1480 (error "The patch buffer wasn't found"))))
1481
fa2eb9ac 1482
3af0304a 1483;; This function executes in meta buffer. It knows where event happened.
bbe6126c 1484(defun ediff-filegroup-action ()
fa2eb9ac
MK
1485 "Execute appropriate action for the selected session."
1486 (interactive)
1487 (let* ((pos (ediff-event-point last-command-event))
1488 (meta-buf (ediff-event-buffer last-command-event))
1489 ;; ediff-get-meta-info gives error if meta-buf or pos are invalid
1490 (info (ediff-get-meta-info meta-buf pos))
328b4b70 1491 (session-buf (ediff-get-session-buffer info))
c004db97 1492 (session-number (ediff-get-session-number-at-pos pos meta-buf))
328b4b70 1493 merge-autostore-dir file1 file2 file3 regexp)
fa2eb9ac 1494
328b4b70 1495 (setq file1 (ediff-get-session-objA-name info)
bbe6126c
MK
1496 file2 (ediff-get-session-objB-name info)
1497 file3 (ediff-get-session-objC-name info))
fa2eb9ac
MK
1498
1499 ;; make sure we don't start on hidden sessions
1500 ;; ?H means marked for hiding. ?I means invalid (hidden).
bbe6126c 1501 (if (memq (ediff-get-session-status info) '(?I))
fa2eb9ac
MK
1502 (progn
1503 (beep)
1504 (if (y-or-n-p "This session is marked as hidden, unmark? ")
1505 (progn
1506 (ediff-set-session-status info nil)
328b4b70 1507 (ediff-update-meta-buffer meta-buf nil session-number))
fa2eb9ac
MK
1508 (error "Aborted"))))
1509
e756eb9f 1510 (ediff-with-current-buffer meta-buf
92c51e07
MK
1511 (setq merge-autostore-dir
1512 (ediff-get-group-merge-autostore-dir ediff-meta-list))
fa2eb9ac
MK
1513 (goto-char pos) ; if the user clicked on session--move point there
1514 ;; First handle sessions involving directories (which are themselves
1515 ;; session groups)
1516 ;; After that handle individual sessions
bd698e98 1517 (cond ((ediff-meta-session-p info)
fa2eb9ac
MK
1518 ;; do ediff/ediff-merge on subdirectories
1519 (if (ediff-buffer-live-p session-buf)
1520 (ediff-show-meta-buffer session-buf)
1521 (setq regexp (read-string "Filter through regular expression: "
1522 nil 'ediff-filtering-regexp-history))
1523 (ediff-directories-internal
1524 file1 file2 file3 regexp
1525 ediff-session-action-function
1526 ediff-metajob-name
3af0304a 1527 ;; make it update (car info) after startup
086171bf
MK
1528 `(list (lambda ()
1529 ;; child session group should know its parent
1530 (setq ediff-parent-meta-buffer
1531 (quote ,ediff-meta-buffer)
1532 ediff-meta-session-number
1533 ,session-number)
1534 ;; and parent will know its child
1535 (setcar (quote ,info) ediff-meta-buffer))))))
fa2eb9ac
MK
1536
1537 ;; Do ediff-revision on a subdirectory
bbe6126c
MK
1538 ((and (ediff-one-filegroup-metajob)
1539 (ediff-revision-metajob)
1540 (file-directory-p file1))
fa2eb9ac
MK
1541 (if (ediff-buffer-live-p session-buf)
1542 (ediff-show-meta-buffer session-buf)
1543 (setq regexp (read-string "Filter through regular expression: "
1544 nil 'ediff-filtering-regexp-history))
1545 (ediff-directory-revisions-internal
1546 file1 regexp
1547 ediff-session-action-function ediff-metajob-name
3af0304a 1548 ;; make it update (car info) after startup
086171bf
MK
1549 `(list (lambda ()
1550 ;; child session group should know its parent and
1551 ;; its number
1552 (setq ediff-parent-meta-buffer
1553 (quote ,ediff-meta-buffer)
1554 ediff-meta-session-number
1555 ,session-number)
1556 ;; and parent will know its child
1557 (setcar (quote ,info) ediff-meta-buffer))))))
fa2eb9ac
MK
1558
1559 ;; From here on---only individual session handlers
1560
bbe6126c 1561 ;; handle an individual session with a live control buffer
fa2eb9ac 1562 ((ediff-buffer-live-p session-buf)
e756eb9f 1563 (ediff-with-current-buffer session-buf
fa2eb9ac
MK
1564 (setq ediff-mouse-pixel-position (mouse-pixel-position))
1565 (ediff-recenter 'no-rehighlight)))
1566
1567 ((ediff-problematic-session-p info)
1568 (beep)
1569 (if (y-or-n-p
3af0304a 1570 "This session has no ancestor. Merge without the ancestor? ")
fa2eb9ac
MK
1571 (ediff-merge-files
1572 file1 file2
bbe6126c 1573 ;; provide startup hooks
086171bf 1574 `(list (lambda ()
3af0304a
MK
1575 (add-hook
1576 'ediff-after-quit-hook-internal
1577 (lambda ()
086171bf 1578 (if (ediff-buffer-live-p ,(current-buffer))
3af0304a 1579 (ediff-show-meta-buffer
086171bf 1580 ,(current-buffer) ,session-number)))
3af0304a 1581 nil 'local)
086171bf 1582 (setq ediff-meta-buffer ,(current-buffer)
328b4b70 1583 ediff-meta-session-number
086171bf 1584 ,session-number)
92c51e07 1585 (setq ediff-merge-store-file
086171bf
MK
1586 ,(if (ediff-nonempty-string-p
1587 merge-autostore-dir)
1588 (concat
1589 merge-autostore-dir
6de3983f 1590 ediff-merge-filename-prefix
086171bf 1591 (file-name-nondirectory file1))
3af0304a 1592 ))
92c51e07
MK
1593 ;; make ediff-startup pass
1594 ;; ediff-control-buffer back to the meta
1595 ;; level; see below
fa2eb9ac 1596 (setcar
086171bf 1597 (quote ,info) ediff-control-buffer))))
fa2eb9ac 1598 (error "Aborted")))
bbe6126c 1599 ((ediff-one-filegroup-metajob) ; needs 1 file arg
fa2eb9ac
MK
1600 (funcall ediff-session-action-function
1601 file1
bbe6126c 1602 ;; provide startup hooks
086171bf
MK
1603 `(list (lambda ()
1604 (add-hook
1605 'ediff-after-quit-hook-internal
1606 (lambda ()
1607 (if (ediff-buffer-live-p
1608 ,(current-buffer))
1609 (ediff-show-meta-buffer
1610 ,(current-buffer)
1611 ,session-number)))
1612 nil 'local)
1613 (setq ediff-meta-buffer ,(current-buffer)
1614 ediff-meta-session-number
1615 ,session-number)
1616 (setq ediff-merge-store-file
1617 ,(if (ediff-nonempty-string-p
1618 merge-autostore-dir)
1619 (concat
1620 merge-autostore-dir
6de3983f 1621 ediff-merge-filename-prefix
086171bf
MK
1622 (file-name-nondirectory file1))) )
1623 ;; make ediff-startup pass
1624 ;; ediff-control-buffer back to the meta
1625 ;; level; see below
1626 (setcar
1627 (quote ,info) ediff-control-buffer)))))
fa2eb9ac
MK
1628 ((not (ediff-metajob3)) ; need 2 file args
1629 (funcall ediff-session-action-function
1630 file1 file2
bbe6126c 1631 ;; provide startup hooks
086171bf
MK
1632 `(list (lambda ()
1633 (add-hook
1634 'ediff-after-quit-hook-internal
1635 (lambda ()
1636 (if (ediff-buffer-live-p
1637 ,(current-buffer))
1638 (ediff-show-meta-buffer
1639 ,(current-buffer)
1640 ,session-number)))
1641 nil 'local)
1642 (setq ediff-meta-buffer ,(current-buffer)
1643 ediff-meta-session-number
1644 ,session-number)
1645 (setq ediff-merge-store-file
1646 ,(if (ediff-nonempty-string-p
1647 merge-autostore-dir)
1648 (concat
1649 merge-autostore-dir
6de3983f 1650 ediff-merge-filename-prefix
086171bf
MK
1651 (file-name-nondirectory file1))) )
1652 ;; make ediff-startup pass
1653 ;; ediff-control-buffer back to the meta
1654 ;; level; see below
1655 (setcar
1656 (quote ,info) ediff-control-buffer)))))
fa2eb9ac
MK
1657 ((ediff-metajob3) ; need 3 file args
1658 (funcall ediff-session-action-function
1659 file1 file2 file3
1660 ;; arrange startup hooks
086171bf
MK
1661 `(list (lambda ()
1662 (add-hook
1663 'ediff-after-quit-hook-internal
1664 (lambda ()
1665 (if (ediff-buffer-live-p
1666 ,(current-buffer))
1667 (ediff-show-meta-buffer
1668 ,(current-buffer)
1669 ,session-number)))
1670 nil 'local)
1671 (setq ediff-merge-store-file
1672 ,(if (ediff-nonempty-string-p
1673 merge-autostore-dir)
1674 (concat
1675 merge-autostore-dir
6de3983f 1676 ediff-merge-filename-prefix
086171bf
MK
1677 (file-name-nondirectory file1))) )
1678 (setq ediff-meta-buffer , (current-buffer)
1679 ediff-meta-session-number
1680 ,session-number)
1681 ;; this arranges that ediff-startup will pass
1682 ;; the value of ediff-control-buffer back to
1683 ;; the meta level, to the record in the meta
1684 ;; list containing the information about the
1685 ;; session associated with that
1686 ;; ediff-control-buffer
1687 (setcar
1688 (quote ,info) ediff-control-buffer)))))
fa2eb9ac
MK
1689 ) ; cond
1690 ) ; eval in meta-buf
1691 ))
1692
1693(defun ediff-registry-action ()
1694 "Switch to a selected session."
1695 (interactive)
1696 (let* ((pos (ediff-event-point last-command-event))
1697 (buf (ediff-event-buffer last-command-event))
1698 (ctl-buf (ediff-get-meta-info buf pos)))
1699
1700 (if (ediff-buffer-live-p ctl-buf)
1701 ;; check if this is ediff-control-buffer or ediff-meta-buffer
e756eb9f 1702 (if (ediff-with-current-buffer ctl-buf
fa2eb9ac
MK
1703 (eq (key-binding "q") 'ediff-quit-meta-buffer))
1704 ;; it's a meta-buffer -- last action should just display it
328b4b70 1705 (ediff-show-meta-buffer ctl-buf t)
fa2eb9ac 1706 ;; it's a session buffer -- invoke go back to session
e756eb9f 1707 (ediff-with-current-buffer ctl-buf
fa2eb9ac
MK
1708 (setq ediff-mouse-pixel-position (mouse-pixel-position))
1709 (ediff-recenter 'no-rehighlight)))
1710 (beep)
1711 (message "You've selected a stale session --- try again")
1712 (ediff-update-registry))
e756eb9f 1713 (ediff-with-current-buffer buf
fa2eb9ac
MK
1714 (goto-char pos))
1715 ))
1716
1717
328b4b70
MK
1718;; If session number is t, means don't update meta buffer
1719(defun ediff-show-meta-buffer (&optional meta-buf session-number)
fa2eb9ac
MK
1720 "Show the session group buffer."
1721 (interactive)
1722 (let (wind frame silent)
1723 (if meta-buf (setq silent t))
1724
1725 (setq meta-buf (or meta-buf ediff-meta-buffer))
1726 (cond ((not (bufferp meta-buf))
1727 (error "This Ediff session is not part of a session group"))
1728 ((not (ediff-buffer-live-p meta-buf))
1729 (error
1730 "Can't find this session's group panel -- session itself is ok")))
1731
328b4b70
MK
1732 (cond ((numberp session-number)
1733 (ediff-update-meta-buffer meta-buf nil session-number))
1734 ;; if session-number is t, don't update
1735 (session-number)
1736 (t (ediff-cleanup-meta-buffer meta-buf)))
1737
e756eb9f 1738 (ediff-with-current-buffer meta-buf
fa2eb9ac
MK
1739 (save-excursion
1740 (cond ((setq wind (ediff-get-visible-buffer-window meta-buf))
1741 (or silent
1742 (message
1743 "Already showing the group panel for this session"))
1744 (set-window-buffer wind meta-buf)
1745 (select-window wind))
1746 ((window-live-p (setq wind ediff-window-C)) ;in merge--merge buf
1747 (set-window-buffer ediff-window-C meta-buf)
1748 (select-window wind))
1749 ((window-live-p (setq wind ediff-window-A))
1750 (set-window-buffer ediff-window-A meta-buf)
1751 (select-window wind))
1752 ((window-live-p (setq wind ediff-window-B))
1753 (set-window-buffer ediff-window-B meta-buf)
1754 (select-window wind))
1755 ((and
1756 (setq wind
1757 (ediff-get-visible-buffer-window ediff-registry-buffer))
1758 (ediff-window-display-p))
1759 (select-window wind)
1760 (other-window 1)
1761 (set-window-buffer (selected-window) meta-buf))
1762 (t (ediff-skip-unsuitable-frames 'ok-unsplittable)
1763 (set-window-buffer (selected-window) meta-buf)))
1764 ))
4ae69eac
MK
1765 (if (and (ediff-window-display-p)
1766 (window-live-p
1767 (setq wind (ediff-get-visible-buffer-window meta-buf))))
fa2eb9ac 1768 (progn
4ae69eac 1769 (setq frame (window-frame wind))
fa2eb9ac
MK
1770 (raise-frame frame)
1771 (ediff-reset-mouse frame)))
1772 (run-hooks 'ediff-show-session-group-hook)
1773 ))
1774
328b4b70
MK
1775(defun ediff-show-current-session-meta-buffer ()
1776 (interactive)
1777 (ediff-show-meta-buffer nil ediff-meta-session-number))
1778
fa2eb9ac
MK
1779(defun ediff-show-meta-buff-from-registry ()
1780 "Display the session group buffer for a selected session group."
1781 (interactive)
1782 (let* ((pos (ediff-event-point last-command-event))
1783 (meta-buf (ediff-event-buffer last-command-event))
1784 (info (ediff-get-meta-info meta-buf pos))
1785 (meta-or-session-buf info))
e756eb9f 1786 (ediff-with-current-buffer meta-or-session-buf
328b4b70 1787 (ediff-show-meta-buffer nil t))))
fa2eb9ac
MK
1788
1789;;;###autoload
1790(defun ediff-show-registry ()
1791 "Display Ediff's registry."
1792 (interactive)
1793 (ediff-update-registry)
1794 (if (not (ediff-buffer-live-p ediff-registry-buffer))
1795 (error "No active Ediff sessions or corrupted session registry"))
1796 (let (wind frame)
1797 ;; for some reason, point moves in ediff-registry-buffer, so we preserve it
4ae69eac 1798 ;; explicitly
e756eb9f 1799 (ediff-with-current-buffer ediff-registry-buffer
fa2eb9ac
MK
1800 (save-excursion
1801 (cond ((setq wind
1802 (ediff-get-visible-buffer-window ediff-registry-buffer))
1803 (message "Already showing the registry")
1804 (set-window-buffer wind ediff-registry-buffer)
1805 (select-window wind))
1806 ((window-live-p ediff-window-C)
1807 (set-window-buffer ediff-window-C ediff-registry-buffer)
1808 (select-window ediff-window-C))
1809 ((window-live-p ediff-window-A)
1810 (set-window-buffer ediff-window-A ediff-registry-buffer)
1811 (select-window ediff-window-A))
1812 ((window-live-p ediff-window-B)
1813 (set-window-buffer ediff-window-B ediff-registry-buffer)
1814 (select-window ediff-window-B))
1815 ((and (setq wind
1816 (ediff-get-visible-buffer-window ediff-meta-buffer))
1817 (ediff-window-display-p))
1818 (select-window wind)
1819 (other-window 1)
1820 (set-window-buffer (selected-window) ediff-registry-buffer))
1821 (t (ediff-skip-unsuitable-frames 'ok-unsplittable)
1822 (set-window-buffer (selected-window) ediff-registry-buffer)))
1823 ))
1824 (if (ediff-window-display-p)
1825 (progn
1826 (setq frame
1827 (window-frame
1828 (ediff-get-visible-buffer-window ediff-registry-buffer)))
1829 (raise-frame frame)
1830 (ediff-reset-mouse frame)))
1831 (run-hooks 'ediff-show-registry-hook)
1832 ))
1833
1834;;;###autoload
1835(defalias 'eregistry 'ediff-show-registry)
1836
3af0304a 1837;; If meta-buf doesn't exist, it is created. In that case, id doesn't have a
fa2eb9ac
MK
1838;; parent meta-buf
1839;; Check if META-BUF exists before calling this function
328b4b70 1840;; Optional MUST-REDRAW, if non-nil, would force redrawal of the whole meta
3af0304a 1841;; buffer. Otherwise, it will just go over the buffer and update activity marks
328b4b70
MK
1842;; and session status.
1843;; SESSION-NUMBER, if specified, says which session caused the update.
1844(defun ediff-update-meta-buffer (meta-buf &optional must-redraw session-number)
1845 (if (ediff-buffer-live-p meta-buf)
1846 (ediff-with-current-buffer meta-buf
3af0304a
MK
1847 (let (overl)
1848 (cond (must-redraw ; completely redraw the meta buffer
1849 (funcall ediff-meta-redraw-function ediff-meta-list))
1850 ((numberp session-number) ; redraw only for the given session
1851 (ediff-update-session-marker-in-dir-meta-buffer
1852 session-number))
1853 (t ; update what changed only, but scan the entire meta buffer
1854 (ediff-update-markers-in-dir-meta-buffer ediff-meta-list)))
1855 (setq overl (ediff-get-meta-overlay-at-pos (point)))
1856 ;; skip the invisible sessions
1857 (while (and overl (ediff-overlay-get overl 'invisible))
1858 (ediff-next-meta-item1)
1859 (setq overl (ediff-get-meta-overlay-at-pos (point))))
1860 ))))
fa2eb9ac
MK
1861
1862(defun ediff-update-registry ()
e756eb9f 1863 (ediff-with-current-buffer (current-buffer)
fa2eb9ac
MK
1864 (if (ediff-buffer-live-p ediff-registry-buffer)
1865 (ediff-redraw-registry-buffer)
1866 (ediff-prepare-meta-buffer
1867 'ediff-registry-action
1868 ediff-session-registry
1869 "*Ediff Registry"
1870 'ediff-redraw-registry-buffer
1871 'ediff-registry))
1872 ))
1873
92c51e07
MK
1874;; If meta-buf exists, it is redrawn along with parent.
1875;; Otherwise, nothing happens.
fa2eb9ac
MK
1876(defun ediff-cleanup-meta-buffer (meta-buffer)
1877 (if (ediff-buffer-live-p meta-buffer)
e756eb9f 1878 (ediff-with-current-buffer meta-buffer
fa2eb9ac
MK
1879 (ediff-update-meta-buffer meta-buffer)
1880 (if (ediff-buffer-live-p ediff-parent-meta-buffer)
328b4b70
MK
1881 (ediff-update-meta-buffer
1882 ediff-parent-meta-buffer nil ediff-meta-session-number)))))
fa2eb9ac 1883
328b4b70 1884;; t if no session is in progress
fa2eb9ac
MK
1885(defun ediff-safe-to-quit (meta-buffer)
1886 (if (ediff-buffer-live-p meta-buffer)
1887 (let ((lis ediff-meta-list)
1888 (cont t)
1889 buffer-read-only)
328b4b70 1890 ;;(ediff-update-meta-buffer meta-buffer)
e756eb9f 1891 (ediff-with-current-buffer meta-buffer
fa2eb9ac
MK
1892 (setq lis (cdr lis)) ; discard the description part of meta-list
1893 (while (and cont lis)
1894 (if (ediff-buffer-live-p
1895 (ediff-get-group-buffer lis)) ; in progress
1896 (setq cont nil))
1897 (setq lis (cdr lis)))
1898 cont))))
1899
1900(defun ediff-quit-meta-buffer ()
92c51e07 1901 "If the group has no active session, delete the meta buffer.
fa2eb9ac
MK
1902If no session is in progress, ask to confirm before deleting meta buffer.
1903Otherwise, bury the meta buffer.
1904If this is a session registry buffer then just bury it."
1905 (interactive)
1906 (let* ((buf (current-buffer))
1907 (dir-diffs-buffer ediff-dir-diffs-buffer)
1908 (meta-diff-buffer ediff-meta-diff-buffer)
328b4b70 1909 (session-number ediff-meta-session-number)
fa2eb9ac
MK
1910 (parent-buf ediff-parent-meta-buffer)
1911 (dont-show-registry (eq buf ediff-registry-buffer)))
1912 (if dont-show-registry
1913 (bury-buffer)
328b4b70 1914 ;;(ediff-cleanup-meta-buffer buf)
fa2eb9ac
MK
1915 (cond ((and (ediff-safe-to-quit buf)
1916 (y-or-n-p "Quit this session group? "))
92c51e07 1917 (run-hooks 'ediff-quit-session-group-hook)
bbe6126c 1918 (message "")
fa2eb9ac
MK
1919 (ediff-dispose-of-meta-buffer buf))
1920 ((ediff-safe-to-quit buf)
1921 (bury-buffer))
1922 (t
bbe6126c
MK
1923 (error
1924 "This session group has active sessions---cannot exit")))
328b4b70 1925 (ediff-update-meta-buffer parent-buf nil session-number)
fa2eb9ac
MK
1926 (ediff-kill-buffer-carefully dir-diffs-buffer)
1927 (ediff-kill-buffer-carefully meta-diff-buffer)
1928 (if (ediff-buffer-live-p parent-buf)
1929 (progn
1930 (setq dont-show-registry t)
328b4b70 1931 (ediff-show-meta-buffer parent-buf session-number)))
fa2eb9ac
MK
1932 )
1933 (or dont-show-registry
1934 (ediff-show-registry))))
1935
1936(defun ediff-dispose-of-meta-buffer (buf)
1937 (setq ediff-session-registry (delq buf ediff-session-registry))
e756eb9f 1938 (ediff-with-current-buffer buf
fa2eb9ac
MK
1939 (if (ediff-buffer-live-p ediff-dir-diffs-buffer)
1940 (kill-buffer ediff-dir-diffs-buffer)))
1941 (kill-buffer buf))
1942
1943
4ae69eac 1944;; Obtain information on a meta record where the user clicked or typed
fa2eb9ac
MK
1945;; BUF is the buffer where this happened and POINT is the position
1946;; If optional NOERROR arg is given, don't report error and return nil if no
1947;; meta info is found on line.
1948(defun ediff-get-meta-info (buf point &optional noerror)
1949 (let (result olist tmp)
1950 (if (and point (ediff-buffer-live-p buf))
e756eb9f 1951 (ediff-with-current-buffer buf
fa2eb9ac
MK
1952 (if ediff-xemacs-p
1953 (setq result
1954 (if (setq tmp (extent-at point buf 'ediff-meta-info))
1955 (ediff-overlay-get tmp 'ediff-meta-info)))
1956 (setq olist (overlays-at point))
1957 (setq olist
3af0304a 1958 (mapcar (lambda (elt) (overlay-get elt 'ediff-meta-info))
fa2eb9ac
MK
1959 olist))
1960 (while (and olist (null (car olist))
1961 (overlay-get (car olist) 'invisible))
1962 (setq olist (cdr olist)))
1963 (setq result (car olist)))))
1964 (if result
1965 result
1966 (if noerror
1967 nil
1968 (ediff-update-registry)
1969 (error "No session info in this line")))))
1970
328b4b70
MK
1971
1972(defun ediff-get-meta-overlay-at-pos (point)
1973 (if ediff-xemacs-p
1974 (extent-at point (current-buffer) 'ediff-meta-info)
1975 (let* ((overl-list (overlays-at point))
1976 (overl (car overl-list)))
1977 (while (and overl (null (overlay-get overl 'ediff-meta-info)))
1978 (setq overl-list (cdr overl-list)
1979 overl (car overl-list)))
1980 overl)))
1981
c004db97
MK
1982(defsubst ediff-get-session-number-at-pos (point &optional meta-buffer)
1983 (setq meta-buffer (if (ediff-buffer-live-p meta-buffer)
1984 meta-buffer
1985 (current-buffer)))
1986 (ediff-with-current-buffer meta-buffer
1987 (ediff-overlay-get
1988 (ediff-get-meta-overlay-at-pos point) 'ediff-meta-session-number)))
328b4b70
MK
1989
1990
1991;; Return location of the next meta overlay after point
fa2eb9ac 1992(defun ediff-next-meta-overlay-start (point)
bbe6126c
MK
1993 (if (eobp)
1994 (goto-char (point-min))
328b4b70 1995 (let ((overl (ediff-get-meta-overlay-at-pos point)))
bbe6126c
MK
1996 (if ediff-xemacs-p
1997 (progn
bbe6126c
MK
1998 (if overl
1999 (setq overl (next-extent overl))
2000 (setq overl (next-extent (current-buffer))))
2001 (if overl
2002 (extent-start-position overl)
2003 (point-max)))
328b4b70 2004 (if overl
bbe6126c
MK
2005 ;; note: end of current overlay is the beginning of the next one
2006 (overlay-end overl)
2007 (next-overlay-change point))))
2008 ))
fa2eb9ac 2009
328b4b70 2010
fa2eb9ac 2011(defun ediff-previous-meta-overlay-start (point)
bbe6126c
MK
2012 (if (bobp)
2013 (goto-char (point-max))
328b4b70 2014 (let ((overl (ediff-get-meta-overlay-at-pos point)))
bbe6126c
MK
2015 (if ediff-xemacs-p
2016 (progn
bbe6126c
MK
2017 (if overl
2018 (setq overl (previous-extent overl))
2019 (setq overl (previous-extent (current-buffer))))
2020 (if overl
2021 (extent-start-position overl)
2022 (point-min)))
328b4b70 2023 (if overl (setq point (overlay-start overl)))
bbe6126c
MK
2024 ;; to get to the beginning of prev overlay
2025 (if (not (bobp))
328b4b70 2026 ;; trick to overcome an emacs bug--doesn't always find previous
bbe6126c
MK
2027 ;; overlay change correctly
2028 (setq point (1- point)))
2029 (setq point (previous-overlay-change point))
2030 ;; If we are not over an overlay after subtracting 1, it means we are
3af0304a 2031 ;; in the description area preceding session records. In this case,
bbe6126c
MK
2032 ;; goto the top of the registry buffer.
2033 (or (car (overlays-at point))
2034 (setq point (point-min)))
328b4b70 2035 point))))
bbe6126c
MK
2036
2037;; this is the action invoked when the user selects a patch from the meta
2038;; buffer.
2039(defun ediff-patch-file-form-meta (file &optional startup-hooks)
2040 (let* ((pos (ediff-event-point last-command-event))
2041 (meta-buf (ediff-event-buffer last-command-event))
2042 ;; ediff-get-meta-info gives error if meta-buf or pos are invalid
2043 (info (ediff-get-meta-info meta-buf pos))
2044 (meta-patchbuf ediff-meta-patchbufer)
2045 session-buf beg-marker end-marker)
2046
2047 (if (or (file-directory-p file) (string-match "/dev/null" file))
2048 (error "`%s' is not an ordinary file" (file-name-as-directory file)))
2049 (setq session-buf (ediff-get-session-buffer info)
2050 beg-marker (ediff-get-session-objB-name info)
2051 end-marker (ediff-get-session-objC-name info))
2052
2053 (or (ediff-buffer-live-p session-buf) ; either an active patch session
2054 (null session-buf) ; or it is a virgin session
2055 (error
2056 "Patch has been already applied to this file--cannot be repeated!"))
2057
e756eb9f 2058 (ediff-with-current-buffer meta-patchbuf
bbe6126c
MK
2059 (save-restriction
2060 (widen)
2061 (narrow-to-region beg-marker end-marker)
2062 (ediff-patch-file-internal meta-patchbuf file startup-hooks)))))
2063
2064
3ec8facd
KH
2065(defun ediff-unmark-all-for-operation ()
2066 "Unmark all sessions marked for operation."
2067 (interactive)
3af0304a
MK
2068 (let ((list (cdr ediff-meta-list))
2069 elt)
3ec8facd
KH
2070 (while (setq elt (car list))
2071 (ediff-mark-session-for-operation elt 'unmark)
2072 (setq list (cdr list))))
2073 (ediff-update-meta-buffer (current-buffer) 'must-redraw))
2074
2075(defun ediff-unmark-all-for-hiding ()
2076 "Unmark all sessions marked for hiding."
2077 (interactive)
3af0304a
MK
2078 (let ((list (cdr ediff-meta-list))
2079 elt)
3ec8facd
KH
2080 (while (setq elt (car list))
2081 (ediff-mark-session-for-hiding elt 'unmark)
2082 (setq list (cdr list))))
2083 (ediff-update-meta-buffer (current-buffer) 'must-redraw))
2084
2085
bbe6126c
MK
2086(defun ediff-meta-mark-equal-files ()
2087 "Run though the session list and mark identical files.
2088This is used only for sessions that involve 2 or 3 files at the same time."
2089 (interactive)
2090 (let ((list (cdr ediff-meta-list))
3ec8facd 2091 marked1 marked2 marked3
bbe6126c 2092 fileinfo1 fileinfo2 fileinfo3 elt)
3af0304a 2093 (message "Comparing files ...")
bbe6126c
MK
2094 (while (setq elt (car list))
2095 (setq fileinfo1 (ediff-get-session-objA elt)
2096 fileinfo2 (ediff-get-session-objB elt)
2097 fileinfo3 (ediff-get-session-objC elt))
2098 (ediff-set-file-eqstatus fileinfo1 nil)
2099 (ediff-set-file-eqstatus fileinfo2 nil)
2100 (ediff-set-file-eqstatus fileinfo3 nil)
2101
3ec8facd
KH
2102 (setq marked1 t
2103 marked2 t
2104 marked3 t)
2105 (or (ediff-mark-if-equal fileinfo1 fileinfo2)
2106 (setq marked1 nil))
bbe6126c
MK
2107 (if (ediff-metajob3)
2108 (progn
3ec8facd
KH
2109 (or (ediff-mark-if-equal fileinfo1 fileinfo3)
2110 (setq marked2 nil))
2111 (or (ediff-mark-if-equal fileinfo2 fileinfo3)
2112 (setq marked3 nil))))
2113 (if (and marked1 marked2 marked3)
2114 (cond ((eq last-command-char ?h)
2115 (ediff-mark-session-for-hiding elt 'mark))
2116 ((eq last-command-char ?m)
2117 (ediff-mark-session-for-operation elt 'mark))
2118 ))
3af0304a
MK
2119 (setq list (cdr list)))
2120 (message "Comparing files ... Done"))
328b4b70 2121 (ediff-update-meta-buffer (current-buffer) 'must-redraw))
bbe6126c
MK
2122
2123;; mark files 1 and 2 as equal, if they are.
3ec8facd 2124;; returns t, if something was marked
bbe6126c 2125(defun ediff-mark-if-equal (fileinfo1 fileinfo2)
328b4b70
MK
2126 (let ((f1 (car fileinfo1))
2127 (f2 (car fileinfo2)))
3ec8facd
KH
2128 (cond ((file-directory-p f1) nil)
2129 ((file-directory-p f2) nil)
2130 ((ediff-same-file-contents f1 f2)
2131 (ediff-set-file-eqstatus fileinfo1 t)
2132 (ediff-set-file-eqstatus fileinfo2 t)
2133 t))
2134 ))
bbe6126c 2135
fa2eb9ac
MK
2136
2137
2138;;; Local Variables:
2139;;; eval: (put 'ediff-defvar-local 'lisp-indent-hook 'defun)
e756eb9f
MK
2140;;; eval: (put 'ediff-with-current-buffer 'lisp-indent-hook 1)
2141;;; eval: (put 'ediff-with-current-buffer 'edebug-form-spec '(form body))
fa2eb9ac
MK
2142;;; End:
2143
eb4daa01 2144;;; ediff-mult.el ends here