(ediff-show-registry): Use renamed file ediff-mult.
[bpt/emacs.git] / lisp / ediff-mult.el
CommitLineData
fa2eb9ac
MK
1;;; ediff-meta.el --- support for multi-file/multi-buffer processing in Ediff
2;;; Copyright (C) 1995 Free Software Foundation, Inc.
3
4;; Author: Michael Kifer <kifer@cs.sunysb.edu>
5
6;; This file is part of GNU Emacs.
7
8;; GNU Emacs is free software; you can redistribute it and/or modify
9;; it under the terms of the GNU General Public License as published by
10;; the Free Software Foundation; either version 2, or (at your option)
11;; any later version.
12
13;; GNU Emacs is distributed in the hope that it will be useful,
14;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16;; GNU General Public License for more details.
17
18;; You should have received a copy of the GNU General Public License
19;; along with GNU Emacs; see the file COPYING. If not, write to
20;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
21
22;;; Notes:
23;;
24;; Users are strongly encourage to add functionality to this file.
25;; In particular, epatch needs to be enhanced to work with multi-file
26;; patches. The present file contains all the infrastructure needed for that.
27;;
28;; Generally, to to implement a new multisession capability within Ediff,
29;; you need to tell it
30;;
31;; 1. How to display the session group buffer.
32;; This function must indicate which Ediff sessions are active (+) and
33;; which are finished (-).
34;; See ediff-redraw-directory-group-buffer for an example.
35;; In all likelihood, ediff-redraw-directory-group-buffer can be used
36;; directly or after a small modification.
37;; 2. What action to take when the user clicks button 2 or types v,e, or
38;; RET. See ediff-dir-action.
39;; 3. Provide a list of pairs or triples of file names (or buffers,
40;; depending on the particular Ediff operation you want to invoke)
41;; in the following format:
42;; ((obj1 obj2 [optional obj3]) (...) ...)
43;; Actually, the format of this list is pretty much up to the
44;; developer. The only thing is that it must be a list of lists.
45;; Also, keep in mind that the function ediff-prepare-meta-buffer
46;; (which see) prepends nil in fron of each list (i.e., the above list
47;; will become ((nil obj1 obj2 ...) (nil ...) ...).
48;; Ediff expects that your function (in 2 above) will arrange to
49;; replace this prepended nil (via setcar) with the actual ediff
50;; control buffer associated with an appropriate Ediff session.
51;; This is arranged through internal startup hooks that can be passed
52;; to any of Ediff major entries (such as ediff-files, epatch, etc.).
53;; See how this is done in ediff-dir-action.
54;; 4. Write a function that makes a call to ediff-prepare-meta-buffer
55;; passing all this info.
56;; You may be able to use ediff-directories-internal as a template.
57;; 5. If you intend to add several related pieces of functionality,
58;; you may want to keep the function in 4 as an internal version
59;; and then write several top-level interactive functions that call it
60;; with different parameters.
61;; See how ediff-directories, ediff-merge-directories, and
62;; ediff-merge-directories-with-ancestor all use
63;; ediff-directories-internal.
64;;
65;; In case of multifile patching, the easiest thing is to first apply the patch
66;; and then find out which files were patched (using the algorithm utilized by
67;; Unix patch and by parsing the patch file). The procedure ediff-patch-file
68;; works for single-file patches only. However, it can deal with remote and
69;; compressed files. Check out ediff-patch-file for details.
70;;
71;; Another useful addition here could be session groups selected by patterns
72;; (which are different in each directory). For instance, one may want to
73;; compare files of the form abc{something}.c to files old{something}.d
74;; which may be in the same or different directories. Or, one may want to
75;; compare all files of the form {something} to files of the form {something}~.
76;;
009650b3
MK
77;; Implementing this requires writing an collating function, which should pair
78;; up appropriate files. It will also require a generalization of the functions
fa2eb9ac
MK
79;; that do the layout of the meta- and differences buffers and of
80;; ediff-dir-action.
81
82(require 'ediff-init)
83
84;; meta-buffer
85(ediff-defvar-local ediff-meta-buffer nil "")
86(ediff-defvar-local ediff-parent-meta-buffer nil "")
87;; the registry buffer
88(defvar ediff-registry-buffer nil)
89
90(defconst ediff-meta-buffer-message "This is an Ediff Session Group Panel: %s
91
92Useful commands:
93 button2, `v', RET over a session line: start that Ediff session
94 `M' in any session invoked from here: bring back this buffer
95 `R':\tdisplay the registry of active Ediff sessions
96 `h':\tmark session for hiding; with prefix arg--unmark
97 `x':\thide marked sessions; with prefix arg--unhide hidden sessions
98 `m':\tmark session for non-hiding operation; with prefix arg--unmark
99 SPC:\tnext session
100 DEL:\tprevious session
101 `E':\tbrowse Ediff on-line manual
102 `q':\tquit this session group
103")
104
105(ediff-defvar-local ediff-meta-buffer-map nil
106 "The keymap for the meta buffer.")
107(defvar ediff-dir-diffs-buffer-map (make-sparse-keymap)
108 "The keymap to be installed in the buffer showing differences between
109directories.")
110
111;; Variable specifying the action to take when the use invokes ediff in the
112;; meta buffer. This is usually ediff-registry-action or ediff-dir-action
113(ediff-defvar-local ediff-meta-action-function nil "")
114;; Tells ediff-update-meta-buffer how to redraw it
115(ediff-defvar-local ediff-meta-redraw-function nil "")
116;; Tells ediff-dir-action and similar procedures how to invoke Ediff for the
117;; sessions in a given session group
118(ediff-defvar-local ediff-session-action-function nil "")
119
120(ediff-defvar-local ediff-metajob-name nil "")
121
122;; buffer used to collect custom diffs from individual sessions in the group
123(ediff-defvar-local ediff-meta-diff-buffer nil "")
124
125;; history var to use for filtering groups
126(defvar ediff-filtering-regexp-history nil "")
127
128;; This has the form ((ctl-buf file1 file2) (stl-buf file1 file2) ...)
129;; If ctl-buf is nil, the file-pare wasn't processed yet. If it is
130;; killed-buffer object, the file pair has been processed. If it is a live
131;; buffer, this means ediff is still working on the pair
132(ediff-defvar-local ediff-meta-list nil "")
133
134
135;; the difference list between directories in a directory session group
136(ediff-defvar-local ediff-dir-difference-list nil "")
137(ediff-defvar-local ediff-dir-diffs-buffer nil "")
138
139;; The registry of Ediff sessions. A list of control buffers.
140(defvar ediff-session-registry nil)
141
142(defvar ediff-registry-setup-hook nil
143 "*Hooks run just after the registry control panel is set up.")
144(defvar ediff-session-group-setup-hook nil
145 "*Hooks run just after a meta-buffer controlling a session group, such as
146ediff-directories, is run.")
147(defvar ediff-show-registry-hook nil
148 "*Hooks run just after the registry buffer is shown.")
149(defvar ediff-show-session-group-hook nil
150 "*Hooks run just after a session group buffer is shown.")
151
152;;; API
153
154(defun ediff-get-group-buffer (meta-list)
155 (nth 0 (car meta-list)))
156(defun ediff-get-group-regexp (meta-list)
157 (nth 1 (car meta-list)))
158(defun ediff-get-group-objA (meta-list)
159 (nth 2 (car meta-list)))
160(defun ediff-get-group-objB (meta-list)
161 (nth 3 (car meta-list)))
162(defun ediff-get-group-objC (meta-list)
163 (nth 4 (car meta-list)))
164(defun ediff-get-session-buffer (elt)
165 (nth 0 elt))
166(defun ediff-get-session-status (elt)
167 (nth 1 elt))
168(defun ediff-get-session-objA (elt)
169 (nth 2 elt))
170(defun ediff-get-session-objB (elt)
171 (nth 3 elt))
172(defun ediff-get-session-objC (elt)
173 (nth 4 elt))
174(defun ediff-set-session-status (session-info new-status)
175 (setcar (cdr session-info) new-status))
176
177;; set up the keymap in the meta buffer
178(defun ediff-setup-meta-map()
179 (setq ediff-meta-buffer-map (make-sparse-keymap))
180 (suppress-keymap ediff-meta-buffer-map)
181 (define-key ediff-meta-buffer-map "q" 'ediff-quit-meta-buffer)
182 (define-key ediff-meta-buffer-map "R" 'ediff-show-registry)
183 (define-key ediff-meta-buffer-map "E" 'ediff-documentation)
184 (define-key ediff-meta-buffer-map "v" ediff-meta-action-function)
185 (define-key ediff-meta-buffer-map "\C-m" ediff-meta-action-function)
186 (define-key ediff-meta-buffer-map " " 'ediff-next-meta-item)
187 (define-key ediff-meta-buffer-map "\C-?" 'ediff-previous-meta-item)
188 (define-key ediff-meta-buffer-map [delete] 'ediff-previous-meta-item)
189 (define-key ediff-meta-buffer-map [backspace] 'ediff-previous-meta-item)
190 (if ediff-no-emacs-help-in-control-buffer
191 (define-key ediff-meta-buffer-map "\C-h" 'ediff-previous-meta-item))
192 (if ediff-emacs-p
193 (define-key ediff-meta-buffer-map [mouse-2] ediff-meta-action-function)
194 (define-key ediff-meta-buffer-map [button2] ediff-meta-action-function))
195
196 (use-local-map ediff-meta-buffer-map))
197
198(defun ediff-meta-mode ()
199 "This mode controls all operations on Ediff session groups.
200It is entered through one of the following commands:
201 `ediff-directories'
202 `edirs'
203 `ediff-directories3'
204 `edirs3'
205 `ediff-merge-directories'
206 `edirs-merge'
207 `ediff-merge-directories-with-ancestor'
208 `edirs-merge-with-ancestor'
209 `ediff-directory-revisions'
210 `edir-revisions'
211 `ediff-merge-directory-revisions'
212 `edir-merge-revisions'
213 `ediff-merge-directory-revisions-with-ancestor'
214 `edir-merge-revisions-with-ancestor'
215
216Commands:
217\\{ediff-meta-buffer-map}"
218 (kill-all-local-variables)
219 (setq major-mode 'ediff-meta-mode)
220 (setq mode-name "MetaEdiff"))
221
222
223;; the keymap for the buffer showing directory differences
224(suppress-keymap ediff-dir-diffs-buffer-map)
225(define-key ediff-dir-diffs-buffer-map "q" 'ediff-bury-dir-diffs-buffer)
226(define-key ediff-dir-diffs-buffer-map " " 'next-line)
227(define-key ediff-dir-diffs-buffer-map "\C-?" 'previous-line)
228(define-key ediff-dir-diffs-buffer-map [delete] 'previous-line)
229(define-key ediff-dir-diffs-buffer-map [backspace] 'previous-line)
230
231(defun ediff-next-meta-item (count)
232 "Move to the next item in Ediff registry or session group buffer.
233Moves in circular fashion. With numeric prefix arg, skip this many items."
234 (interactive "p")
235 (or count (setq count 1))
236 (while (< 0 count)
237 (setq count (1- count))
238 (ediff-next-meta-item1)))
239
240;; Move to the next meta item
241(defun ediff-next-meta-item1 ()
242 (let (pos)
243 (setq pos (ediff-next-meta-overlay-start (point)))
244;;; ;; skip deleted
245;;; (while (memq (ediff-get-session-status
246;;; (ediff-get-meta-info (current-buffer) pos 'noerror))
247;;; '(?H ?I))
248;;; (setq pos (ediff-next-meta-overlay-start pos)))
249
250 (if pos (goto-char pos))
251 (if (eq ediff-metajob-name 'ediff-registry)
252 (if (search-forward "*Ediff" nil t)
253 (skip-chars-backward "a-zA-Z*"))
254 (if (> (skip-chars-forward "-+?H* \t0-9") 0)
255 (backward-char 1)))))
256
257
258(defun ediff-previous-meta-item (count)
259 "Move to the previous item in Ediff registry or session group buffer.
260Moves in circular fashion. With numeric prefix arg, skip this many items."
261 (interactive "p")
262 (or count (setq count 1))
263 (while (< 0 count)
264 (setq count (1- count))
265 (ediff-previous-meta-item1)))
266
267(defun ediff-previous-meta-item1 ()
268 (let (pos)
269 (setq pos (ediff-previous-meta-overlay-start (point)))
270;;; ;; skip deleted
271;;; (while (ediff-get-session-status
272;;; (ediff-get-meta-info (current-buffer) pos 'noerror))
273;;; (setq pos (ediff-previous-meta-overlay-start pos)))
274
275 (if pos (goto-char pos))
276 (if (eq ediff-metajob-name 'ediff-registry)
277 (if (search-forward "*Ediff" nil t)
278 (skip-chars-backward "a-zA-Z*"))
279 (if (> (skip-chars-forward "-+?H* \t0-9") 0)
280 (backward-char 1)))))
281
282
283
284;; DIR1, DIR2, DIR3 are directories.
285;; REGEXP is a regexp used to filter
286;; files in the directories.
287;; If a file is a directory in dir1 but not dir2 (or vice versa), it is not
288;; included in the intersection. However, a regular file that is a dir in dir3
289;; is included, since dir3 files are supposed to be ancestors for merging.
290;; Returns a list of the form:
291;; ((dir1 dir2 dir3) (f1 f2 f3) (f1 f2 f3) ...)
292;; dir3, f3 can be nil if intersecting only 2 directories.
293;; If COMPARISON-FUNC is given, use it. Otherwise, use string=
294;; DIFF-VAR is contains the name of the variable in which to return the
295;; difference list. The diff list is of the form:
296;; ((dir1 dir2 dir3) (file . num) (file . num)...)
297;; where num encodes the set of dirs where the file is found:
298;; 2 - only dir1; 3 - only dir2; 5 - only dir3; 6 - dir1&2; 10 - dir1&3; etc.
299(defun ediff-intersect-directories (jobname diff-var regexp dir1 dir2
300 &optional dir3 comparison-func)
301 (require 'cl)
302 (setq comparison-func (or comparison-func 'string=))
303 (let (lis1 lis2 lis3 common auxdir1 auxdir2 auxdir3 difflist)
304
305 (setq auxdir1 (file-name-as-directory dir1)
306 lis1 (directory-files auxdir1 nil regexp)
307 auxdir2 (file-name-as-directory dir2)
308 lis2 (directory-files auxdir2 nil regexp))
309
310 (if (stringp dir3)
311 (setq auxdir3 (file-name-as-directory dir3)
312 lis3 (directory-files auxdir3 nil regexp)))
313
314 (setq lis1 (delete "." lis1)
315 lis1 (delete ".." lis1))
316
317 (setq common (intersection lis1 lis2 :test comparison-func))
318 ;; get rid of files that are directories in dir1 but not dir2
319 (mapcar (function (lambda (elt)
320 (if (Xor (file-directory-p (concat auxdir1 elt))
321 (file-directory-p (concat auxdir2 elt)))
322 (setq common (delq elt common)))))
323 common)
324 ;; intersect with the third dir
325 (if lis3 (setq common (intersection common lis3 :test comparison-func)))
326 (if (ediff-comparison-metajob3 jobname)
327 (mapcar (function (lambda (elt)
328 (if (Xor (file-directory-p (concat auxdir1 elt))
329 (file-directory-p (concat auxdir3 elt)))
330 (setq common (delq elt common)))))
331 common))
332
333 ;; trying to avoid side effects of sorting
334 (setq common (sort (copy-list common) 'string-lessp))
335
336 ;; compute difference list
337 (setq difflist (set-difference
338 (union (union lis1 lis2 :test comparison-func)
339 lis3
340 :test comparison-func)
341 common
342 :test comparison-func)
343 difflist (delete "." difflist)
344 ;; copy-list needed because sort sorts it by side effects
345 difflist (sort (copy-list (delete ".." difflist)) 'string-lessp))
346
347 (setq difflist (mapcar (function (lambda (elt) (cons elt 1))) difflist))
348
349 ;; check for files belonging to lis1/2/3
350 (mapcar (function (lambda (elt)
351 (if (member (car elt) lis1)
352 (setcdr elt (* (cdr elt) 2)))
353 (if (member (car elt) lis2)
354 (setcdr elt (* (cdr elt) 3)))
355 (if (member (car elt) lis3)
356 (setcdr elt (* (cdr elt) 5)))
357 ))
358 difflist)
359 (setq difflist (cons (list regexp auxdir1 auxdir2 auxdir3) difflist))
360
361 (set diff-var difflist)
362
363 ;; return result
364 (cons (list regexp auxdir1 auxdir2 auxdir3)
365 (mapcar (function (lambda (elt)
366 (list (concat auxdir1 elt)
367 (concat auxdir2 elt)
368 (if lis3
369 (concat auxdir3 elt)))))
370 common))
371 ))
372
373;; find directory files that are under revision.
374;; display subdirectories, too, since we may visit them recursively.
375(defun ediff-get-directory-files-under-revision (jobname regexp dir1)
376 (require 'cl)
377 (let (lis1 elt common auxdir1)
378 (setq auxdir1 (file-name-as-directory dir1)
379 lis1 (directory-files auxdir1 nil regexp))
380
381 (while lis1
382 (setq elt (car lis1)
383 lis1 (cdr lis1))
384 ;; take files under revision control
385 (cond ((file-directory-p (concat auxdir1 elt))
386 (setq common (cons elt common)))
387 ((file-exists-p (concat auxdir1 elt ",v"))
388 (setq common (cons elt common))))
389 ) ; while
390
391 (setq common (delete "." common)
392 common (delete ".." common))
393
394 ;; trying to avoid side effects of sorting
395 (setq common (sort (copy-list common) 'string-lessp))
396
397 ;; return result
398 (cons (list regexp auxdir1 nil nil)
399 (mapcar (function (lambda (elt)
400 (list (concat auxdir1 elt)
401 nil nil)))
402 common))
403 ))
404
405
406;; If file groups selected by patterns will ever be implemented, this
407;; comparison function might become useful.
408;;;; uses external variables PAT1 PAT2 to compare str1/2
409;;;; patterns must be of the form ???*???? where ??? are strings of chars
410;;;; containing no *.
411;;(defun ediff-pattern= (str1 str2)
412;; (let (pos11 pos12 pos21 pos22 len1 len2)
413;; (setq pos11 0
414;; len (length epat1)
415;; pos12 len)
416;; (while (and (< pos11 len) (not (= (aref epat1 pos11) ?*)))
417;; (setq pos11 (1+ pos11)))
418;; (while (and (> pos12 0) (not (= (aref epat1 (1- pos12)) ?*)))
419;; (setq pos12 (1- pos12)))
420;;
421;; (setq pos21 0
422;; len (length epat2)
423;; pos22 len)
424;; (while (and (< pos21 len) (not (= (aref epat2 pos21) ?*)))
425;; (setq pos21 (1+ pos21)))
426;; (while (and (> pos22 0) (not (= (aref epat2 (1- pos22)) ?*)))
427;; (setq pos22 (1- pos22)))
428;;
429;; (if (and (> (length str1) pos12) (>= pos12 pos11) (> pos11 -1)
430;; (> (length str2) pos22) (>= pos22 pos21) (> pos21 -1))
431;; (string= (substring str1 pos11 pos12)
432;; (substring str2 pos21 pos22)))
433;; ))
434
435
436;; Prepare meta-buffer in accordance with the argument-function and
437;; redraw-function. Must return the created meta-buffer.
438(defun ediff-prepare-meta-buffer (action-func meta-list
439 meta-buffer-name redraw-function
440 jobname &optional startup-hooks)
441 (let* ((meta-buffer-name
442 (ediff-unique-buffer-name meta-buffer-name "*"))
443 (meta-buffer (get-buffer-create meta-buffer-name)))
444 (ediff-eval-in-buffer meta-buffer
445
446 ;; comes first
447 (ediff-meta-mode)
448
449 (setq ediff-meta-action-function action-func
450 ediff-meta-redraw-function redraw-function
451 ediff-metajob-name jobname
452 ediff-meta-buffer meta-buffer)
453
454 ;; comes after ediff-meta-action-function is set
455 (ediff-setup-meta-map)
456
457 (if (eq ediff-metajob-name 'ediff-registry)
458 (progn
459 (setq ediff-registry-buffer meta-buffer
460 ediff-meta-list meta-list)
461 ;; this func is used only from registry buffer, not from other
462 ;; meta-buffs.
463 (define-key
464 ediff-meta-buffer-map "M" 'ediff-show-meta-buff-from-registry))
465 ;; initialize the meta list -- don't do this for registry we prepend
466 ;; '(nil nil) nil to all elts of meta-list, except the first. The
467 ;; first nil will later be replaced by the session buffer. The second
468 ;; is reserved for session status.
469 ;; (car ediff-meta-list) gets cons'ed with the session group buffer.
470 (setq ediff-meta-list
471 (cons (cons meta-buffer (car meta-list))
472 (mapcar (function
473 (lambda (elt)
474 (cons nil (cons nil elt))))
475 (cdr meta-list)))))
476
477 (or (eq meta-buffer ediff-registry-buffer)
478 (setq ediff-session-registry
479 (cons meta-buffer ediff-session-registry)))
480
481 ;; redraw-function uses ediff-meta-list
482 (funcall redraw-function ediff-meta-list)
483
484 ;; set read-only/non-modified
485 (setq buffer-read-only t)
486 (set-buffer-modified-p nil)
487
488 (run-hooks 'startup-hooks)
489 ;; arrange for showing directory contents differences
490 ;; must be after run startup-hooks, since ediff-dir-difference-list is
491 ;; set inside these hooks
492 (if (eq action-func 'ediff-dir-action)
493 (progn
494 ;; put meta buffer in (car ediff-dir-difference-list)
495 (setq ediff-dir-difference-list
496 (cons (cons meta-buffer (car ediff-dir-difference-list))
497 (cdr ediff-dir-difference-list)))
498
499 (or (ediff-dir1-metajob jobname)
500 (ediff-draw-dir-diffs ediff-dir-difference-list))
501 (define-key ediff-meta-buffer-map "h" 'ediff-mark-for-hiding)
502 (define-key
503 ediff-meta-buffer-map "x" 'ediff-hide-marked-sessions)
504 (define-key ediff-meta-buffer-map "m" 'ediff-mark-for-operation)
505 (if (ediff-collect-diffs-metajob jobname)
506 (define-key
507 ediff-meta-buffer-map "P" 'ediff-collect-custom-diffs))
508 (define-key ediff-meta-buffer-map "u" 'ediff-up-meta-hierarchy)
509 (define-key ediff-meta-buffer-map "D" 'ediff-show-dir-diffs)))
510
511 (if (eq ediff-metajob-name 'ediff-registry)
512 (run-hooks 'ediff-registry-setup-hook)
513 (run-hooks 'ediff-session-group-setup-hook))
514 ) ; eval in meta-buffer
515 meta-buffer))
516
517
518;; this is a setup function for ediff-directories
519;; must return meta-buffer
520(defun ediff-redraw-directory-group-buffer (meta-list)
521 ;; extract directories
522 (let ((meta-buf (ediff-get-group-buffer meta-list))
523 (empty t)
524 (sessionNum 0)
525 regexp elt session-buf f1 f2 f3 pt
526 point tmp-list buffer-read-only)
527 (ediff-eval-in-buffer meta-buf
528 (setq point (point))
529 (erase-buffer)
530 (insert (format ediff-meta-buffer-message
531 (ediff-abbrev-jobname ediff-metajob-name)))
532
533 (setq regexp (ediff-get-group-regexp meta-list))
534
535 (if (ediff-collect-diffs-metajob)
536 (insert
537 " `P':\tcollect custom diffs of all marked sessions\n"))
538 (insert
539 " `u':\tshow parent session group
540 `D':\tdisplay differences among the contents of directories\n\n")
541
542 (if (and (stringp regexp) (> (length regexp) 0))
543 (insert (format "Filter-through regular expression: %s\n" regexp)))
544
545 (insert "\n
546 Size Name
547 -----------------------------------------------------------------------
548
549")
550
551 ;; discard info on directories and regexp
552 (setq meta-list (cdr meta-list)
553 tmp-list meta-list)
554 (while (and tmp-list empty)
555 (if (and (car tmp-list)
556 (not (eq (ediff-get-session-status (car tmp-list)) ?I)))
557 (setq empty nil))
558 (setq tmp-list (cdr tmp-list)))
559
560 (if empty
561 (insert
562 " ****** ****** This session group has no members\n"))
563
564 ;; now organize file names like this:
565 ;; preferred format:
566 ;; use-mark sizeA dateA sizeB dateB filename
567 ;; I don't have time to mess up with calculating last modtimes
568 ;; (XEmacs has no decode-time function), so
569 ;; the actual format is:
570 ;; use-mark Size filename
571 ;; make sure directories are displayed with a trailing slash.
572 ;; If one is a directory and another isn't, indicate this with a `?'
573 (while meta-list
574 (setq elt (car meta-list)
575 meta-list (cdr meta-list)
576 sessionNum (1+ sessionNum))
577 (if (eq (ediff-get-session-status elt) ?I)
578 ()
579 (setq session-buf (ediff-get-session-buffer elt)
580 f1 (ediff-get-session-objA elt)
581 f2 (ediff-get-session-objB elt)
582 f3 (ediff-get-session-objC elt))
583 (setq pt (point))
584 ;; insert markers
585 (insert (cond ((null session-buf) " ") ; virgin session
586 ((ediff-buffer-live-p session-buf) "+") ;active session
587 (t "-"))) ; finished session
588 (insert (cond ((ediff-get-session-status elt)) ; session has status,
589 ;;; e.g., ?H, ?I
590 (t " "))) ; normal session
591 (insert " Session " (int-to-string sessionNum) ":\n")
592 (ediff-meta-insert-file-info f1)
593 (ediff-meta-insert-file-info f2)
594 (ediff-meta-insert-file-info f3)
595 (ediff-set-meta-overlay pt (point) elt)))
596 (set-buffer-modified-p nil)
597 (goto-char point)
598 meta-buf)))
599
600;; Check if this is a problematic session.
601;; Return nil if not. Otherwise, return symbol representing the problem
602;; At present, problematic sessions occur only in -with-ancestor comparisons
603;; when the ancestor is a directory rather than a file.
604(defun ediff-problematic-session-p (session)
605 (let ((f1 (ediff-get-session-objA session))
606 (f2 (ediff-get-session-objB session))
607 (f3 (ediff-get-session-objC session)))
608 (cond ((and (stringp f1) (not (file-directory-p f1))
609 (stringp f2) (not (file-directory-p f2))
610 (stringp f3) (file-directory-p f3)
611 (ediff-ancestor-metajob))
612 ;; more may be added later
613 'ancestor-is-dir)
614 (t nil))))
615
616(defun ediff-meta-insert-file-info (file)
617 (if (stringp file)
618 (insert
619 (format
620 " %10d %s\n"
621 (nth 7 (file-attributes file))
622 ;; dir names in meta lists have no trailing `/' so insert it
623 (cond ((file-directory-p file)
624 (file-name-as-directory (ediff-abbreviate-file-name file)))
625 (t (ediff-abbreviate-file-name file)))))
626 ))
627
628
629
630(defun ediff-draw-dir-diffs (diff-list)
631 (if (null diff-list) (error "Lost difference info on these directories"))
632 (let* ((buf-name (ediff-unique-buffer-name
633 "*Ediff File Group Differences" "*"))
634 (regexp (ediff-get-group-regexp diff-list))
635 (dir1 (ediff-abbreviate-file-name (ediff-get-group-objA diff-list)))
636 (dir2 (ediff-abbreviate-file-name (ediff-get-group-objB diff-list)))
637 (dir3 (ediff-get-group-objC diff-list))
638 (dir3 (if (stringp dir3) (ediff-abbreviate-file-name dir3)))
639 (meta-buf (ediff-get-group-buffer diff-list))
640 (underline (make-string 26 ?-))
641 file code
642 buffer-read-only)
643 ;; skip the directory part
644 (setq diff-list (cdr diff-list))
645 (setq ediff-dir-diffs-buffer (get-buffer-create buf-name))
646 (ediff-eval-in-buffer ediff-dir-diffs-buffer
647 (use-local-map ediff-dir-diffs-buffer-map)
648 (erase-buffer)
649 (setq ediff-meta-buffer meta-buf)
650 (insert "\t\t*** Directory Differences ***\n")
651 (insert "
652Useful commands:
653 `q': hide this buffer
654 SPC: next line
655 DEL: previous line\n\n")
656
657 (if (and (stringp regexp) (> (length regexp) 0))
658 (insert (format "Filter-through regular expression: %s\n" regexp)))
659 (insert "\n")
660 (insert (format "\n%-27s%-26s"
661 (ediff-truncate-string-left
662 (ediff-abbreviate-file-name
663 (file-name-as-directory dir1))
664 25)
665 (ediff-truncate-string-left
666 (ediff-abbreviate-file-name
667 (file-name-as-directory dir2))
668 25)))
669 (if dir3
670 (insert (format " %-25s\n"
671 (ediff-truncate-string-left
672 (ediff-abbreviate-file-name
673 (file-name-as-directory dir3))
674 25)))
675 (insert "\n"))
676 (insert (format "%s%s" underline underline))
677 (if (stringp dir3)
678 (insert (format "%s\n\n" underline))
679 (insert "\n\n"))
680
681 (if (null diff-list)
682 (insert "\n\t*** No differences ***\n"))
683
684 (while diff-list
685 (setq file (car (car diff-list))
686 code (cdr (car diff-list))
687 diff-list (cdr diff-list))
688 (if (= (mod code 2) 0) ; dir1
689 (insert (format "%-27s"
690 (ediff-truncate-string-left
691 (ediff-abbreviate-file-name
692 (if (file-directory-p (concat dir1 file))
693 (file-name-as-directory file)
694 file))
695 24)))
696 (insert (format "%-27s" "---")))
697 (if (= (mod code 3) 0) ; dir2
698 (insert (format "%-26s"
699 (ediff-truncate-string-left
700 (ediff-abbreviate-file-name
701 (if (file-directory-p (concat dir2 file))
702 (file-name-as-directory file)
703 file))
704 24)))
705 (insert (format "%-26s" "---")))
706 (if (stringp dir3)
707 (if (= (mod code 5) 0) ; dir3
708 (insert (format " %-25s"
709 (ediff-truncate-string-left
710 (ediff-abbreviate-file-name
711 (if (file-directory-p (concat dir3 file))
712 (file-name-as-directory file)
713 file))
714 24)))
715 (insert (format " %-25s" "---"))))
716 (insert "\n"))
717 (setq buffer-read-only t)
718 (set-buffer-modified-p nil)) ; eval in diff buffer
719 ))
720
721(defun ediff-bury-dir-diffs-buffer ()
722 "Bury the directory difference buffer. Display the meta buffer instead."
723 (interactive)
724 (let ((buf ediff-meta-buffer)
725 wind)
726 (bury-buffer)
727 (if (setq wind (ediff-get-visible-buffer-window buf))
728 (select-window wind)
729 (set-window-buffer (selected-window) buf))))
730
731;; executes in dir session group buffer
732;; show buffer differences
733(defun ediff-show-dir-diffs ()
734 "Display differences among the directories involved in session group."
735 (interactive)
736 (if (ediff-dir1-metajob)
737 (error "This command is inapplicable in the present context"))
738 (or (ediff-buffer-live-p ediff-dir-diffs-buffer)
739 (ediff-draw-dir-diffs ediff-dir-difference-list))
740 (let ((buf ediff-dir-diffs-buffer))
741 (other-window 1)
742 (set-window-buffer (selected-window) buf)
743 (goto-char (point-min))))
744
745(defun ediff-up-meta-hierarchy ()
746 "Go to the parent session group buffer."
747 (interactive)
748 (if (ediff-buffer-live-p ediff-parent-meta-buffer)
749 (ediff-show-meta-buffer ediff-parent-meta-buffer)
750 (error "This session group has no parent")))
751
752
753;; argument is ignored
754(defun ediff-redraw-registry-buffer (&optional ignore)
755 (ediff-eval-in-buffer ediff-registry-buffer
756 (let ((point (point))
757 elt bufAname bufBname bufCname cur-diff total-diffs pt
758 job-name meta-list registry-list buffer-read-only)
759 (erase-buffer)
760 (insert "This is a registry of all active Ediff sessions.
761
762Useful commands:
763 button2, `v', RET over a session record: switch to that session
764 `M' over a session record: display the associated session group
765 `R' in any Ediff session: display session registry
766 SPC:\tnext session
767 DEL:\tprevious session
768 `E':\tbrowse Ediff on-line manual
769 `q':\tbury registry
770
771
772\t\tActive Ediff Sessions:
773\t\t----------------------
774
775")
776 ;; purge registry list from dead buffers
777 (mapcar (function (lambda (elt)
778 (if (not (ediff-buffer-live-p elt))
779 (setq ediff-session-registry
780 (delq elt ediff-session-registry)))))
781 ediff-session-registry)
782
783 (if (null ediff-session-registry)
784 (insert " ******* No active Ediff sessions *******\n"))
785
786 (setq registry-list ediff-session-registry)
787 (while registry-list
788 (setq elt (car registry-list)
789 registry-list (cdr registry-list))
790
791 (if (ediff-buffer-live-p elt)
792 (if (ediff-eval-in-buffer elt
793 (setq job-name ediff-metajob-name
794 meta-list ediff-meta-list)
795 (and ediff-metajob-name
796 (not (eq ediff-metajob-name 'ediff-registry))))
797 (progn
798 (setq pt (point))
799 (insert (format " *group*\t%s: %s\n"
800 (buffer-name elt)
801 (ediff-abbrev-jobname job-name)))
802 (insert (format "\t\t %s %s %s\n"
803 (ediff-abbreviate-file-name
804 (ediff-get-group-objA meta-list))
805 (ediff-abbreviate-file-name
806 (or (ediff-get-group-objB meta-list) ""))
807 (ediff-abbreviate-file-name
808 (or (ediff-get-group-objC meta-list) ""))))
809 (ediff-set-meta-overlay pt (point) elt))
810 (progn
811 (ediff-eval-in-buffer elt
812 (setq bufAname (if (ediff-buffer-live-p ediff-buffer-A)
813 (buffer-name ediff-buffer-A)
814 "!!!killed buffer!!!")
815 bufBname (if (ediff-buffer-live-p ediff-buffer-B)
816 (buffer-name ediff-buffer-B)
817 "!!!killed buffer!!!")
818 bufCname (cond ((not (ediff-3way-job))
819 "")
820 ((ediff-buffer-live-p ediff-buffer-C)
821 (buffer-name ediff-buffer-C))
822 (t "!!!killed buffer!!!")))
823 (setq total-diffs (format "%-4d" ediff-number-of-differences)
824 cur-diff
825 (cond ((= ediff-current-difference -1) " _")
826 ((= ediff-current-difference
827 ediff-number-of-differences)
828 " $")
829 (t (format
830 "%4d" (1+ ediff-current-difference))))
831 job-name ediff-job-name))
832 ;; back in the meta buf
833 (setq pt (point))
834 (insert cur-diff "/" total-diffs "\t"
835 (buffer-name elt)
836 (format ": %s" (ediff-abbrev-jobname job-name)))
837 (insert
838 "\n\t\t " bufAname " " bufBname " " bufCname "\n")
839 (ediff-set-meta-overlay pt (point) elt))))
840 ) ; while
841 (set-buffer-modified-p nil)
842 (goto-char point)
843 )))
844
845;; sets overlay around a meta record with 'ediff-meta-info property PROP
846(defun ediff-set-meta-overlay (b e prop)
847 (let (overl)
848 (setq overl (ediff-make-overlay b e))
849 (if ediff-emacs-p
850 (ediff-overlay-put overl 'mouse-face 'highlight)
851 (ediff-overlay-put overl 'highlight t))
852 (ediff-overlay-put overl 'ediff-meta-info prop)))
853
854(defun ediff-mark-for-hiding (unmark)
855 "Mark session for hiding. With prefix arg, unmark."
856 (interactive "P")
857 (let* ((pos (ediff-event-point last-command-event))
858 (meta-buf (ediff-event-buffer last-command-event))
859 ;; ediff-get-meta-info gives error if meta-buf or pos are invalid
860 (info (ediff-get-meta-info meta-buf pos))
861 (session-buf (ediff-get-session-buffer info)))
862
863 (if unmark
864 (ediff-set-session-status info nil)
865 (if (ediff-buffer-live-p session-buf)
866 (error "Can't hide active session, %s" (buffer-name session-buf)))
867 (ediff-set-session-status info ?H))
868 (ediff-update-meta-buffer meta-buf)
869 ))
870
871(defun ediff-mark-for-operation (unmark)
872 "Mark session for a group operation. With prefix arg, unmark."
873 (interactive "P")
874 (let* ((pos (ediff-event-point last-command-event))
875 (meta-buf (ediff-event-buffer last-command-event))
876 ;; ediff-get-meta-info gives error if meta-buf or pos are invalid
877 (info (ediff-get-meta-info meta-buf pos)))
878
879 (if unmark
880 (ediff-set-session-status info nil)
881 (ediff-set-session-status info ?*))
882 (ediff-update-meta-buffer meta-buf)
883 ))
884
885(defun ediff-hide-marked-sessions (unhide)
886 "Hide marked sessions. With prefix arg, unhide."
887 (interactive "P")
888 (let ((grp-buf (ediff-get-group-buffer ediff-meta-list))
889 (meta-list (cdr ediff-meta-list))
890 (from (if unhide ?I ?H))
891 (to (if unhide ?H ?I))
892 (numMarked 0)
893 elt)
894 (while meta-list
895 (setq elt (car meta-list)
896 meta-list (cdr meta-list))
897 (if (eq (ediff-get-session-status elt) from)
898 (progn
899 (setq numMarked (1+ numMarked))
900 (ediff-set-session-status elt to))))
901 (if (> numMarked 0)
902 (ediff-update-meta-buffer grp-buf)
903 (beep)
904 (if unhide
905 (message "Nothing to reveal...")
906 (message "Nothing to hide...")))
907 ))
908
909;; Apply OPERATION to marked sessions. Operation expects one argument of type
910;; meta-list member (not the first one), i.e., a regular session description.
911;; Returns number of marked sessions on which operation was performed
912(defun ediff-operate-on-marked-sessions (operation)
913 (let ((grp-buf (ediff-get-group-buffer ediff-meta-list))
914 (meta-list (cdr ediff-meta-list))
915 (marksym ?*)
916 (numMarked 0)
917 (sessionNum 0)
918 elt)
919 (while meta-list
920 (setq elt (car meta-list)
921 meta-list (cdr meta-list)
922 sessionNum (1+ sessionNum))
923 (if (eq (ediff-get-session-status elt) marksym)
924 (save-excursion
925 (setq numMarked (1+ numMarked))
926 (funcall operation elt sessionNum))))
927 (ediff-update-meta-buffer grp-buf) ; just in case
928 numMarked
929 ))
930
931(defun ediff-append-custom-diff (session sessionNum)
932 (or (ediff-collect-diffs-metajob)
933 (error "Sorry, I don't do this for everyone..."))
934 (let ((session-buf (ediff-get-session-buffer session))
935 (meta-diff-buff ediff-meta-diff-buffer)
936 (metajob ediff-metajob-name)
937 tmp-buf custom-diff-buf)
938 (if (ediff-buffer-live-p session-buf)
939 (ediff-eval-in-buffer session-buf
940 (if (eq ediff-control-buffer session-buf) ; individual session
941 (setq custom-diff-buf ediff-custom-diff-buffer))))
942
943 (or (ediff-buffer-live-p meta-diff-buff)
944 (error "Ediff: something wrong--no multiple diffs buffer"))
945
946 (cond ((ediff-buffer-live-p custom-diff-buf)
947 (save-excursion
948 (set-buffer meta-diff-buff)
949 (goto-char (point-max))
950 (insert-buffer custom-diff-buf)
951 (insert "\n")))
952 ((eq metajob 'ediff-directories)
953 ;; get diffs by calling shell command on ediff-custom-diff-program
954 (save-excursion
955 (set-buffer (setq tmp-buf (get-buffer-create ediff-tmp-buffer)))
956 (erase-buffer)
957 (shell-command
958 (format "%s %s %s %s"
959 ediff-custom-diff-program ediff-custom-diff-options
960 (ediff-get-session-objA session)
961 (ediff-get-session-objB session))
962 t))
963 (save-excursion
964 (set-buffer meta-diff-buff)
965 (goto-char (point-max))
966 (insert-buffer tmp-buf)
967 (insert "\n")))
968 (t
969 (error
970 "Session %d is marked but inactive--can't make its diff"
971 sessionNum)))
972 ))
973
974(defun ediff-collect-custom-diffs ()
975 "Collect custom diffs of marked sessions in buffer `*Ediff Multifile Diffs*'.
976This operation is defined only for `ediff-directories' and
977`ediff-directory-revisions', since its intent is to produce
978multifile patches. For `ediff-directory-revisions', we insist that
979all marked sessions must be active."
980 (interactive)
981 (or (ediff-buffer-live-p ediff-meta-diff-buffer)
982 (setq ediff-meta-diff-buffer
983 (get-buffer-create
984 (ediff-unique-buffer-name "*Ediff Multifile Diffs" "*"))))
985 (ediff-eval-in-buffer ediff-meta-diff-buffer
986 (erase-buffer))
987 (if (> (ediff-operate-on-marked-sessions 'ediff-append-custom-diff) 0)
988 ;; did something
989 (display-buffer ediff-meta-diff-buffer 'not-this-window)
990 (beep)
991 (message "No marked sessions found")))
992
993
994;; This function executes in meta buffer. It knows where event happened.
995(defun ediff-dir-action ()
996 "Execute appropriate action for the selected session."
997 (interactive)
998 (let* ((pos (ediff-event-point last-command-event))
999 (meta-buf (ediff-event-buffer last-command-event))
1000 ;; ediff-get-meta-info gives error if meta-buf or pos are invalid
1001 (info (ediff-get-meta-info meta-buf pos))
1002 session-buf file1 file2 file3 regexp)
1003
1004 (setq session-buf (ediff-get-session-buffer info)
1005 file1 (ediff-get-session-objA info)
1006 file2 (ediff-get-session-objB info)
1007 file3 (ediff-get-session-objC info))
1008
1009 ;; make sure we don't start on hidden sessions
1010 ;; ?H means marked for hiding. ?I means invalid (hidden).
1011 (if (memq (ediff-get-session-status info) '(?H ?I))
1012 (progn
1013 (beep)
1014 (if (y-or-n-p "This session is marked as hidden, unmark? ")
1015 (progn
1016 (ediff-set-session-status info nil)
1017 (ediff-update-meta-buffer meta-buf))
1018 (error "Aborted"))))
1019
1020 (ediff-eval-in-buffer meta-buf
1021 (goto-char pos) ; if the user clicked on session--move point there
1022 ;; First handle sessions involving directories (which are themselves
1023 ;; session groups)
1024 ;; After that handle individual sessions
1025 (cond ((and (file-directory-p file1)
1026 (stringp file2) (file-directory-p file2)
1027 (if (stringp file3) (file-directory-p file1) t))
1028 ;; do ediff/ediff-merge on subdirectories
1029 (if (ediff-buffer-live-p session-buf)
1030 (ediff-show-meta-buffer session-buf)
1031 (setq regexp (read-string "Filter through regular expression: "
1032 nil 'ediff-filtering-regexp-history))
1033 (ediff-directories-internal
1034 file1 file2 file3 regexp
1035 ediff-session-action-function
1036 ediff-metajob-name
1037 ;; make it update car info after startup
1038 (` (list (lambda ()
1039 ;; child session group should know its parent
1040 (setq ediff-parent-meta-buffer
1041 (quote (, ediff-meta-buffer)))
1042 ;; and parent will know its child
1043 (setcar (quote (, info)) ediff-meta-buffer)))))))
1044
1045 ;; Do ediff-revision on a subdirectory
1046 ((and (ediff-dir1-metajob) (file-directory-p file1))
1047 (if (ediff-buffer-live-p session-buf)
1048 (ediff-show-meta-buffer session-buf)
1049 (setq regexp (read-string "Filter through regular expression: "
1050 nil 'ediff-filtering-regexp-history))
1051 (ediff-directory-revisions-internal
1052 file1 regexp
1053 ediff-session-action-function ediff-metajob-name
1054 ;; make it update car info after startup
1055 (` (list (lambda ()
1056 ;; child session group should know its parent
1057 (setq ediff-parent-meta-buffer
1058 (quote (, ediff-meta-buffer)))
1059 ;; and parent will know its child
1060 (setcar (quote (, info)) ediff-meta-buffer)))))))
1061
1062 ;; From here on---only individual session handlers
1063
1064 ;; handle an individual session with live control buffer
1065 ((ediff-buffer-live-p session-buf)
1066 (ediff-eval-in-buffer session-buf
1067 (setq ediff-mouse-pixel-position (mouse-pixel-position))
1068 (ediff-recenter 'no-rehighlight)))
1069
1070 ((ediff-problematic-session-p info)
1071 (beep)
1072 (if (y-or-n-p
1073 "This session's ancestor is a directory, merge without the ancestor? ")
1074 (ediff-merge-files
1075 file1 file2
1076 ;; arrange startup hooks
1077 (` (list (lambda ()
1078 (setq ediff-meta-buffer (, (current-buffer)))
1079 ;; see below for the explanation of what this does
1080 (setcar
1081 (quote (, info)) ediff-control-buffer)))))
1082 (error "Aborted")))
1083 ((ediff-dir1-metajob) ; needs 1 file arg
1084 (funcall ediff-session-action-function
1085 file1
1086 ;; arrange startup hooks
1087 (` (list (lambda ()
1088 (setq ediff-meta-buffer (, (current-buffer)))
1089 ;; see below for explanation of what this does
1090 (setcar
1091 (quote (, info)) ediff-control-buffer))))))
1092 ((not (ediff-metajob3)) ; need 2 file args
1093 (funcall ediff-session-action-function
1094 file1 file2
1095 ;; arrange startup hooks
1096 (` (list (lambda ()
1097 (setq ediff-meta-buffer (, (current-buffer)))
1098 ;; this makes ediff-startup pass the value of
1099 ;; ediff-control-buffer back to the meta
1100 ;; level, to the record in the meta list
1101 ;; containing the information about the
1102 ;; session associated with that
1103 ;; ediff-control-buffer
1104 (setcar
1105 (quote (, info)) ediff-control-buffer))))))
1106 ((ediff-metajob3) ; need 3 file args
1107 (funcall ediff-session-action-function
1108 file1 file2 file3
1109 ;; arrange startup hooks
1110 (` (list (lambda ()
1111 (setq ediff-meta-buffer (, (current-buffer)))
1112 (setcar
1113 (quote (, info)) ediff-control-buffer))))))
1114 ) ; cond
1115 ) ; eval in meta-buf
1116 ))
1117
1118(defun ediff-registry-action ()
1119 "Switch to a selected session."
1120 (interactive)
1121 (let* ((pos (ediff-event-point last-command-event))
1122 (buf (ediff-event-buffer last-command-event))
1123 (ctl-buf (ediff-get-meta-info buf pos)))
1124
1125 (if (ediff-buffer-live-p ctl-buf)
1126 ;; check if this is ediff-control-buffer or ediff-meta-buffer
1127 (if (ediff-eval-in-buffer ctl-buf
1128 (eq (key-binding "q") 'ediff-quit-meta-buffer))
1129 ;; it's a meta-buffer -- last action should just display it
1130 (ediff-show-meta-buffer ctl-buf)
1131 ;; it's a session buffer -- invoke go back to session
1132 (ediff-eval-in-buffer ctl-buf
1133 (setq ediff-mouse-pixel-position (mouse-pixel-position))
1134 (ediff-recenter 'no-rehighlight)))
1135 (beep)
1136 (message "You've selected a stale session --- try again")
1137 (ediff-update-registry))
1138 (ediff-eval-in-buffer buf
1139 (goto-char pos))
1140 ))
1141
1142
1143(defun ediff-show-meta-buffer (&optional meta-buf)
1144 "Show the session group buffer."
1145 (interactive)
1146 (let (wind frame silent)
1147 (if meta-buf (setq silent t))
1148
1149 (setq meta-buf (or meta-buf ediff-meta-buffer))
1150 (cond ((not (bufferp meta-buf))
1151 (error "This Ediff session is not part of a session group"))
1152 ((not (ediff-buffer-live-p meta-buf))
1153 (error
1154 "Can't find this session's group panel -- session itself is ok")))
1155
1156 (ediff-cleanup-meta-buffer meta-buf)
1157 (ediff-eval-in-buffer meta-buf
1158 (save-excursion
1159 (cond ((setq wind (ediff-get-visible-buffer-window meta-buf))
1160 (or silent
1161 (message
1162 "Already showing the group panel for this session"))
1163 (set-window-buffer wind meta-buf)
1164 (select-window wind))
1165 ((window-live-p (setq wind ediff-window-C)) ;in merge--merge buf
1166 (set-window-buffer ediff-window-C meta-buf)
1167 (select-window wind))
1168 ((window-live-p (setq wind ediff-window-A))
1169 (set-window-buffer ediff-window-A meta-buf)
1170 (select-window wind))
1171 ((window-live-p (setq wind ediff-window-B))
1172 (set-window-buffer ediff-window-B meta-buf)
1173 (select-window wind))
1174 ((and
1175 (setq wind
1176 (ediff-get-visible-buffer-window ediff-registry-buffer))
1177 (ediff-window-display-p))
1178 (select-window wind)
1179 (other-window 1)
1180 (set-window-buffer (selected-window) meta-buf))
1181 (t (ediff-skip-unsuitable-frames 'ok-unsplittable)
1182 (set-window-buffer (selected-window) meta-buf)))
1183 ))
1184 (if (ediff-window-display-p)
1185 (progn
1186 (setq frame
1187 (window-frame (ediff-get-visible-buffer-window meta-buf)))
1188 (raise-frame frame)
1189 (ediff-reset-mouse frame)))
1190 (run-hooks 'ediff-show-session-group-hook)
1191 ))
1192
1193(defun ediff-show-meta-buff-from-registry ()
1194 "Display the session group buffer for a selected session group."
1195 (interactive)
1196 (let* ((pos (ediff-event-point last-command-event))
1197 (meta-buf (ediff-event-buffer last-command-event))
1198 (info (ediff-get-meta-info meta-buf pos))
1199 (meta-or-session-buf info))
1200 (ediff-eval-in-buffer meta-or-session-buf
1201 (ediff-show-meta-buffer))))
1202
1203;;;###autoload
1204(defun ediff-show-registry ()
1205 "Display Ediff's registry."
1206 (interactive)
1207 (ediff-update-registry)
1208 (if (not (ediff-buffer-live-p ediff-registry-buffer))
1209 (error "No active Ediff sessions or corrupted session registry"))
1210 (let (wind frame)
1211 ;; for some reason, point moves in ediff-registry-buffer, so we preserve it
1212 ;; explicity
1213 (ediff-eval-in-buffer ediff-registry-buffer
1214 (save-excursion
1215 (cond ((setq wind
1216 (ediff-get-visible-buffer-window ediff-registry-buffer))
1217 (message "Already showing the registry")
1218 (set-window-buffer wind ediff-registry-buffer)
1219 (select-window wind))
1220 ((window-live-p ediff-window-C)
1221 (set-window-buffer ediff-window-C ediff-registry-buffer)
1222 (select-window ediff-window-C))
1223 ((window-live-p ediff-window-A)
1224 (set-window-buffer ediff-window-A ediff-registry-buffer)
1225 (select-window ediff-window-A))
1226 ((window-live-p ediff-window-B)
1227 (set-window-buffer ediff-window-B ediff-registry-buffer)
1228 (select-window ediff-window-B))
1229 ((and (setq wind
1230 (ediff-get-visible-buffer-window ediff-meta-buffer))
1231 (ediff-window-display-p))
1232 (select-window wind)
1233 (other-window 1)
1234 (set-window-buffer (selected-window) ediff-registry-buffer))
1235 (t (ediff-skip-unsuitable-frames 'ok-unsplittable)
1236 (set-window-buffer (selected-window) ediff-registry-buffer)))
1237 ))
1238 (if (ediff-window-display-p)
1239 (progn
1240 (setq frame
1241 (window-frame
1242 (ediff-get-visible-buffer-window ediff-registry-buffer)))
1243 (raise-frame frame)
1244 (ediff-reset-mouse frame)))
1245 (run-hooks 'ediff-show-registry-hook)
1246 ))
1247
1248;;;###autoload
1249(defalias 'eregistry 'ediff-show-registry)
1250
1251;; If meta-buf doesn't exist, it is created. In that case, id doesn't have a
1252;; parent meta-buf
1253;; Check if META-BUF exists before calling this function
1254(defun ediff-update-meta-buffer (meta-buf)
1255 (ediff-eval-in-buffer (current-buffer)
1256 (if (ediff-buffer-live-p meta-buf)
1257 (ediff-eval-in-buffer meta-buf
1258 (funcall ediff-meta-redraw-function ediff-meta-list))
1259 )))
1260
1261(defun ediff-update-registry ()
1262 (ediff-eval-in-buffer (current-buffer)
1263 (if (ediff-buffer-live-p ediff-registry-buffer)
1264 (ediff-redraw-registry-buffer)
1265 (ediff-prepare-meta-buffer
1266 'ediff-registry-action
1267 ediff-session-registry
1268 "*Ediff Registry"
1269 'ediff-redraw-registry-buffer
1270 'ediff-registry))
1271 ))
1272
1273;; if meta-buf exists, it is redrawn along with parent. Otherwise, nothing
1274;; happens
1275(defun ediff-cleanup-meta-buffer (meta-buffer)
1276 (if (ediff-buffer-live-p meta-buffer)
1277 (ediff-eval-in-buffer meta-buffer
1278 (ediff-update-meta-buffer meta-buffer)
1279 (if (ediff-buffer-live-p ediff-parent-meta-buffer)
1280 (ediff-update-meta-buffer ediff-parent-meta-buffer)))))
1281
1282;; t if no session in progress
1283(defun ediff-safe-to-quit (meta-buffer)
1284 (if (ediff-buffer-live-p meta-buffer)
1285 (let ((lis ediff-meta-list)
1286 (cont t)
1287 buffer-read-only)
1288 (ediff-update-meta-buffer meta-buffer)
1289 (ediff-eval-in-buffer meta-buffer
1290 (setq lis (cdr lis)) ; discard the description part of meta-list
1291 (while (and cont lis)
1292 (if (ediff-buffer-live-p
1293 (ediff-get-group-buffer lis)) ; in progress
1294 (setq cont nil))
1295 (setq lis (cdr lis)))
1296 cont))))
1297
1298(defun ediff-quit-meta-buffer ()
1299 "If no unprocessed sessions in the group, delete the meta buffer.
1300If no session is in progress, ask to confirm before deleting meta buffer.
1301Otherwise, bury the meta buffer.
1302If this is a session registry buffer then just bury it."
1303 (interactive)
1304 (let* ((buf (current-buffer))
1305 (dir-diffs-buffer ediff-dir-diffs-buffer)
1306 (meta-diff-buffer ediff-meta-diff-buffer)
1307 (parent-buf ediff-parent-meta-buffer)
1308 (dont-show-registry (eq buf ediff-registry-buffer)))
1309 (if dont-show-registry
1310 (bury-buffer)
1311 (ediff-cleanup-meta-buffer buf)
1312 (cond ((and (ediff-safe-to-quit buf)
1313 (y-or-n-p "Quit this session group? "))
1314 (ediff-dispose-of-meta-buffer buf))
1315 ((ediff-safe-to-quit buf)
1316 (bury-buffer))
1317 (t
1318 (bury-buffer)
1319 (beep)
1320 (message
1321 "Group has active sessions, panel not deleted")))
1322 (ediff-cleanup-meta-buffer parent-buf)
1323 (ediff-kill-buffer-carefully dir-diffs-buffer)
1324 (ediff-kill-buffer-carefully meta-diff-buffer)
1325 (if (ediff-buffer-live-p parent-buf)
1326 (progn
1327 (setq dont-show-registry t)
1328 (ediff-show-meta-buffer parent-buf)))
1329 )
1330 (or dont-show-registry
1331 (ediff-show-registry))))
1332
1333(defun ediff-dispose-of-meta-buffer (buf)
1334 (setq ediff-session-registry (delq buf ediff-session-registry))
1335 (ediff-eval-in-buffer buf
1336 (if (ediff-buffer-live-p ediff-dir-diffs-buffer)
1337 (kill-buffer ediff-dir-diffs-buffer)))
1338 (kill-buffer buf))
1339
1340
1341;; obtain information on a meta record where the user clicked or typed
1342;; BUF is the buffer where this happened and POINT is the position
1343;; If optional NOERROR arg is given, don't report error and return nil if no
1344;; meta info is found on line.
1345(defun ediff-get-meta-info (buf point &optional noerror)
1346 (let (result olist tmp)
1347 (if (and point (ediff-buffer-live-p buf))
1348 (ediff-eval-in-buffer buf
1349 (if ediff-xemacs-p
1350 (setq result
1351 (if (setq tmp (extent-at point buf 'ediff-meta-info))
1352 (ediff-overlay-get tmp 'ediff-meta-info)))
1353 (setq olist (overlays-at point))
1354 (setq olist
1355 (mapcar (function (lambda (elt)
1356 (overlay-get elt 'ediff-meta-info)))
1357 olist))
1358 (while (and olist (null (car olist))
1359 (overlay-get (car olist) 'invisible))
1360 (setq olist (cdr olist)))
1361 (setq result (car olist)))))
1362 (if result
1363 result
1364 (if noerror
1365 nil
1366 (ediff-update-registry)
1367 (error "No session info in this line")))))
1368
1369;; return location of the next meta overlay after point
1370(defun ediff-next-meta-overlay-start (point)
1371 (let (overl)
1372 (if ediff-xemacs-p
1373 (progn
1374 (setq overl (extent-at point (current-buffer) 'ediff-meta-info))
1375 (if overl
1376 (setq overl (next-extent overl))
1377 (setq overl (next-extent (current-buffer))))
1378 (if overl
1379 (extent-start-position overl)
1380 (point-max)))
1381 (if (= point (point-max)) (setq point (point-min)))
1382 (setq overl (car (overlays-at point)))
1383 (if (and overl (overlay-get overl 'ediff-meta-info))
1384 (overlay-end overl)
1385 (next-overlay-change point)))))
1386
1387(defun ediff-previous-meta-overlay-start (point)
1388 (let (overl)
1389 (if ediff-xemacs-p
1390 (progn
1391 (setq overl (extent-at point (current-buffer) 'ediff-meta-info))
1392 (if overl
1393 (setq overl (previous-extent overl))
1394 (setq overl (previous-extent (current-buffer))))
1395 (if overl
1396 (extent-start-position overl)
1397 (point-max)))
1398 ;;(if (bobp) (setq point (point-max)))
1399 (setq overl (car (overlays-at point)))
1400 (setq point (if (and overl (overlay-get overl 'ediff-meta-info))
1401 (previous-overlay-change (overlay-start overl))
1402 (previous-overlay-change point)))
1403 (if (= point (point-min)) (point-max) point)
1404 )))
1405
1406
1407;;; Local Variables:
1408;;; eval: (put 'ediff-defvar-local 'lisp-indent-hook 'defun)
1409;;; eval: (put 'ediff-eval-in-buffer 'lisp-indent-hook 1)
1410;;; End:
1411
1412(provide 'ediff-meta)
1413(require 'ediff-util)
1414
1415;;; ediff-meta.el ends here