Remove several obsolete vars and functions unlikely to be still in use.
[bpt/emacs.git] / lisp / vc / emerge.el
CommitLineData
737e3892 1;;; emerge.el --- merge diffs under Emacs control
151e4b9c 2
737e3892
RS
3;;; The author has placed this file in the public domain.
4
e8af40ee
PJ
5;; This file is part of GNU Emacs.
6
c0932fbc 7;; Author: Dale R. Worley <worley@world.std.com>
9766adfb 8;; Keywords: unix, vc, tools
151e4b9c
RS
9
10;; This software was created by Dale R. Worley and is
11;; distributed free of charge. It is placed in the public domain and
12;; permission is granted to anyone to use, duplicate, modify and redistribute
13;; it provided that this notice is attached.
14
15;; Dale R. Worley provides absolutely NO WARRANTY OF ANY KIND
16;; with respect to this software. The entire risk as to the quality and
17;; performance of this software is with the user. IN NO EVENT WILL DALE
18;; R. WORLEY BE LIABLE TO ANYONE FOR ANY DAMAGES ARISING OUT THE
19;; USE OF THIS SOFTWARE, INCLUDING, WITHOUT LIMITATION, DAMAGES RESULTING FROM
20;; LOST DATA OR LOST PROFITS, OR FOR ANY SPECIAL, INCIDENTAL OR CONSEQUENTIAL
21;; DAMAGES.
22
e8af40ee
PJ
23;;; Commentary:
24
737e3892 25;;; Code:
3b4a6e27 26
3945a3a3
JB
27;; There aren't really global variables, just dynamic bindings
28(defvar A-begin)
29(defvar A-end)
30(defvar B-begin)
31(defvar B-end)
3945a3a3
JB
32(defvar diff-vector)
33(defvar merge-begin)
34(defvar merge-end)
3945a3a3 35(defvar valid-diff)
bbf40036 36
3b4a6e27
JB
37;;; Macros
38
71296446
JB
39(defmacro emerge-defvar-local (var value doc)
40 "Defines SYMBOL as an advertised variable.
737e3892 41Performs a defvar, then executes `make-variable-buffer-local' on
6ff86ec4 42the variable. Also sets the `permanent-local' property, so that
71296446 43`kill-all-local-variables' (called by major-mode setting commands)
737e3892 44won't destroy Emerge control variables."
8a946354
SS
45 `(progn
46 (defvar ,var ,value ,doc)
47 (make-variable-buffer-local ',var)
6ff86ec4 48 (put ',var 'permanent-local t)))
3b4a6e27
JB
49
50;; Add entries to minor-mode-alist so that emerge modes show correctly
ff5f6ddd
RS
51(defvar emerge-minor-modes-list
52 '((emerge-mode " Emerge")
53 (emerge-fast-mode " F")
54 (emerge-edit-mode " E")
55 (emerge-auto-advance " A")
56 (emerge-skip-prefers " S")))
3b4a6e27
JB
57(if (not (assq 'emerge-mode minor-mode-alist))
58 (setq minor-mode-alist (append emerge-minor-modes-list
59 minor-mode-alist)))
60
61;; We need to define this function so describe-mode can describe Emerge mode.
62(defun emerge-mode ()
737e3892
RS
63 "Emerge mode is used by the Emerge file-merging package.
64It is entered only through one of the functions:
65 `emerge-files'
66 `emerge-files-with-ancestor'
67 `emerge-buffers'
68 `emerge-buffers-with-ancestor'
69 `emerge-files-command'
70 `emerge-files-with-ancestor-command'
71 `emerge-files-remote'
72 `emerge-files-with-ancestor-remote'
3b4a6e27
JB
73
74Commands:
75\\{emerge-basic-keymap}
737e3892
RS
76Commands must be prefixed by \\<emerge-fast-keymap>\\[emerge-basic-keymap] in `edit' mode,
77but can be invoked directly in `fast' mode.")
3b4a6e27 78
3b4a6e27
JB
79;;; Emerge configuration variables
80
33933d45
AS
81(defgroup emerge nil
82 "Merge diffs under Emacs control."
83 :group 'tools)
84
3b4a6e27
JB
85;; Commands that produce difference files
86;; All that can be configured is the name of the programs to execute
87;; (emerge-diff-program and emerge-diff3-program) and the options
88;; to be provided (emerge-diff-options). The order in which the file names
89;; are given is fixed.
90;; The file names are always expanded (see expand-file-name) before being
71296446 91;; passed to diff, thus they need not be invoked under a shell that
737e3892 92;; understands `~'.
3b4a6e27
JB
93;; The code which processes the diff/diff3 output depends on all the
94;; finicky details of their output, including the somewhat strange
95;; way they number lines of a file.
33933d45 96(defcustom emerge-diff-program "diff"
9201cc28 97 "Name of the program which compares two files."
33933d45
AS
98 :type 'string
99 :group 'emerge)
100(defcustom emerge-diff3-program "diff3"
9201cc28 101 "Name of the program which compares three files.
33933d45
AS
102Its arguments are the ancestor file and the two variant files."
103 :type 'string
104 :group 'emerge)
105(defcustom emerge-diff-options ""
9201cc28 106 "Options to pass to `emerge-diff-program' and `emerge-diff3-program'."
33933d45
AS
107 :type 'string
108 :group 'emerge)
109(defcustom emerge-match-diff-line
110 (let ((x "\\([0-9]+\\)\\(\\|,\\([0-9]+\\)\\)"))
111 (concat "^" x "\\([acd]\\)" x "$"))
9201cc28 112 "Pattern to match lines produced by diff that describe differences.
33933d45
AS
113This is as opposed to lines from the source files."
114 :type 'regexp
115 :group 'emerge)
116(defcustom emerge-diff-ok-lines-regexp
3b4a6e27 117 "^\\([0-9,]+[acd][0-9,]+$\\|[<>] \\|---\\)"
9201cc28 118 "Regexp that matches normal output lines from `emerge-diff-program'.
33933d45
AS
119Lines that do not match are assumed to be error messages."
120 :type 'regexp
121 :group 'emerge)
122(defcustom emerge-diff3-ok-lines-regexp
3b4a6e27 123 "^\\([1-3]:\\|====\\| \\)"
9201cc28 124 "Regexp that matches normal output lines from `emerge-diff3-program'.
33933d45
AS
125Lines that do not match are assumed to be error messages."
126 :type 'regexp
127 :group 'emerge)
128
129(defcustom emerge-rcs-ci-program "ci"
9201cc28 130 "Name of the program that checks in RCS revisions."
33933d45
AS
131 :type 'string
132 :group 'emerge)
133(defcustom emerge-rcs-co-program "co"
9201cc28 134 "Name of the program that checks out RCS revisions."
33933d45
AS
135 :type 'string
136 :group 'emerge)
137
138(defcustom emerge-process-local-variables nil
9201cc28 139 "Non-nil if Emerge should process local-variables lists in merge buffers.
737e3892 140\(You can explicitly request processing the local-variables
33933d45
AS
141by executing `(hack-local-variables)'.)"
142 :type 'boolean
143 :group 'emerge)
144(defcustom emerge-execute-line-deletions nil
9201cc28 145 "If non-nil: `emerge-execute-line' makes no output if an input was deleted.
737e3892
RS
146It concludes that an input version has been deleted when an ancestor entry
147is present, only one A or B entry is present, and an output entry is present.
151e4b9c 148If nil: In such circumstances, the A or B file that is present will be
33933d45
AS
149copied to the designated output file."
150 :type 'boolean
151 :group 'emerge)
151e4b9c 152
33933d45 153(defcustom emerge-before-flag "vvvvvvvvvvvvvvvvvvvv\n"
9201cc28 154 "Flag placed above the highlighted block of code. Must end with newline.
ff5f6ddd 155Must be set before Emerge is loaded, or emerge-new-flags must be run
33933d45
AS
156after setting."
157 :type 'string
158 :group 'emerge)
159(defcustom emerge-after-flag "^^^^^^^^^^^^^^^^^^^^\n"
9201cc28 160 "Flag placed below the highlighted block of code. Must end with newline.
ff5f6ddd 161Must be set before Emerge is loaded, or emerge-new-flags must be run
33933d45
AS
162after setting."
163 :type 'string
164 :group 'emerge)
ff5f6ddd 165
151e4b9c
RS
166;; Hook variables
167
33933d45 168(defcustom emerge-startup-hook nil
9201cc28 169 "Hook to run in the merge buffer after the merge has been set up."
33933d45
AS
170 :type 'hook
171 :group 'emerge)
172(defcustom emerge-select-hook nil
9201cc28 173 "Hook to run after a difference has been selected.
33933d45
AS
174The variable `n' holds the (internal) number of the difference."
175 :type 'hook
176 :group 'emerge)
177(defcustom emerge-unselect-hook nil
9201cc28 178 "Hook to run after a difference has been unselected.
33933d45
AS
179The variable `n' holds the (internal) number of the difference."
180 :type 'hook
181 :group 'emerge)
151e4b9c
RS
182
183;; Variables to control the default directories of the arguments to
184;; Emerge commands.
185
33933d45 186(defcustom emerge-default-last-directories nil
9201cc28 187 "If nil, default dir for filenames in emerge is `default-directory'.
151e4b9c 188If non-nil, filenames complete in the directory of the last argument of the
33933d45
AS
189same type to an `emerge-files...' command."
190 :type 'boolean
191 :group 'emerge)
151e4b9c
RS
192
193(defvar emerge-last-dir-A nil
737e3892 194 "Last directory for the first file of an `emerge-files...' command.")
151e4b9c 195(defvar emerge-last-dir-B nil
737e3892 196 "Last directory for the second file of an `emerge-files...' command.")
151e4b9c 197(defvar emerge-last-dir-ancestor nil
737e3892 198 "Last directory for the ancestor file of an `emerge-files...' command.")
151e4b9c 199(defvar emerge-last-dir-output nil
737e3892 200 "Last directory for the output file of an `emerge-files...' command.")
151e4b9c 201(defvar emerge-last-revision-A nil
737e3892 202 "Last RCS revision used for first file of an `emerge-revisions...' command.")
151e4b9c 203(defvar emerge-last-revision-B nil
737e3892 204 "Last RCS revision used for second file of an `emerge-revisions...' command.")
151e4b9c 205(defvar emerge-last-revision-ancestor nil
737e3892 206 "Last RCS revision used for ancestor file of an `emerge-revisions...' command.")
151e4b9c 207
ff5f6ddd
RS
208(defvar emerge-before-flag-length)
209(defvar emerge-before-flag-lines)
210(defvar emerge-before-flag-match)
211(defvar emerge-after-flag-length)
212(defvar emerge-after-flag-lines)
213(defvar emerge-after-flag-match)
214(defvar emerge-diff-buffer)
215(defvar emerge-diff-error-buffer)
216(defvar emerge-prefix-argument)
217(defvar emerge-file-out)
218(defvar emerge-exit-func)
219(defvar emerge-globalized-difference-list)
220(defvar emerge-globalized-number-of-differences)
221
3b4a6e27
JB
222;; The flags used to mark differences in the buffers.
223
224;; These function definitions need to be up here, because they are used
225;; during loading.
226(defun emerge-new-flags ()
737e3892
RS
227 "Function to be called after `emerge-{before,after}-flag'.
228This is called after these functions are changed to compute values that
229depend on the flags."
3b4a6e27
JB
230 (setq emerge-before-flag-length (length emerge-before-flag))
231 (setq emerge-before-flag-lines
737e3892 232 (emerge-count-matches-string emerge-before-flag "\n"))
3b4a6e27
JB
233 (setq emerge-before-flag-match (regexp-quote emerge-before-flag))
234 (setq emerge-after-flag-length (length emerge-after-flag))
235 (setq emerge-after-flag-lines
737e3892 236 (emerge-count-matches-string emerge-after-flag "\n"))
3b4a6e27 237 (setq emerge-after-flag-match (regexp-quote emerge-after-flag)))
737e3892
RS
238
239(defun emerge-count-matches-string (string regexp)
3b4a6e27
JB
240 "Return the number of matches in STRING for REGEXP."
241 (let ((i 0)
242 (count 0))
243 (while (string-match regexp string i)
244 (setq count (1+ count))
245 (setq i (match-end 0)))
246 count))
247
3b4a6e27
JB
248;; Calculate dependent variables
249(emerge-new-flags)
250
33933d45 251(defcustom emerge-min-visible-lines 3
9201cc28 252 "Number of lines that we want to show above and below the flags when we are
33933d45
AS
253displaying a difference."
254 :type 'integer
255 :group 'emerge)
3b4a6e27 256
33933d45 257(defcustom emerge-temp-file-prefix
e4a6b88e 258 (expand-file-name "emerge" temporary-file-directory)
9201cc28 259 "Prefix to put on Emerge temporary file names.
e4a6b88e 260Do not start with `~/' or `~USERNAME/'."
33933d45
AS
261 :type 'string
262 :group 'emerge)
3b4a6e27 263
33933d45 264(defcustom emerge-temp-file-mode 384 ; u=rw only
9201cc28 265 "Mode for Emerge temporary files."
33933d45
AS
266 :type 'integer
267 :group 'emerge)
3b4a6e27 268
33933d45 269(defcustom emerge-combine-versions-template
504621b9 270 "#ifdef NEW\n%b#else /* not NEW */\n%a#endif /* not NEW */\n"
9201cc28 271 "Template for `emerge-combine-versions' to combine the two versions.
3b4a6e27
JB
272The template is inserted as a string, with the following interpolations:
273 %a the A version of the difference
274 %b the B version of the difference
737e3892 275 %% the character `%'
3b4a6e27
JB
276Don't forget to end the template with a newline.
277Note that this variable can be made local to a particular merge buffer by
33933d45
AS
278giving a prefix argument to `emerge-set-combine-versions-template'."
279 :type 'string
280 :group 'emerge)
3b4a6e27
JB
281
282;; Build keymaps
283
284(defvar emerge-basic-keymap nil
285 "Keymap of Emerge commands.
737e3892
RS
286Directly available in `fast' mode;
287must be prefixed by \\<emerge-fast-keymap>\\[emerge-basic-keymap] in `edit' mode.")
3b4a6e27
JB
288
289(defvar emerge-fast-keymap nil
737e3892 290 "Local keymap used in Emerge `fast' mode.
3b4a6e27
JB
291Makes Emerge commands directly available.")
292
ff5f6ddd
RS
293(defvar emerge-options-menu
294 (make-sparse-keymap "Options"))
295
296(defvar emerge-merge-menu
297 (make-sparse-keymap "Merge"))
298
299(defvar emerge-move-menu
300 (make-sparse-keymap "Move"))
301
33933d45 302(defcustom emerge-command-prefix "\C-c\C-c"
9201cc28 303 "Command prefix for Emerge commands in `edit' mode.
33933d45
AS
304Must be set before Emerge is loaded."
305 :type 'string
306 :group 'emerge)
3b4a6e27
JB
307
308;; This function sets up the fixed keymaps. It is executed when the first
309;; Emerge is done to allow the user maximum time to set up the global keymap.
310(defun emerge-setup-fixed-keymaps ()
311 ;; Set up the basic keymap
312 (setq emerge-basic-keymap (make-keymap))
313 (suppress-keymap emerge-basic-keymap) ; this sets 0..9 to digit-argument and
314 ; - to negative-argument
315 (define-key emerge-basic-keymap "p" 'emerge-previous-difference)
316 (define-key emerge-basic-keymap "n" 'emerge-next-difference)
317 (define-key emerge-basic-keymap "a" 'emerge-select-A)
318 (define-key emerge-basic-keymap "b" 'emerge-select-B)
319 (define-key emerge-basic-keymap "j" 'emerge-jump-to-difference)
661d3230 320 (define-key emerge-basic-keymap "." 'emerge-find-difference)
3b4a6e27 321 (define-key emerge-basic-keymap "q" 'emerge-quit)
adca3f58 322 (define-key emerge-basic-keymap "\C-]" 'emerge-abort)
3b4a6e27
JB
323 (define-key emerge-basic-keymap "f" 'emerge-fast-mode)
324 (define-key emerge-basic-keymap "e" 'emerge-edit-mode)
325 (define-key emerge-basic-keymap "s" nil)
326 (define-key emerge-basic-keymap "sa" 'emerge-auto-advance)
327 (define-key emerge-basic-keymap "ss" 'emerge-skip-prefers)
328 (define-key emerge-basic-keymap "l" 'emerge-recenter)
329 (define-key emerge-basic-keymap "d" nil)
330 (define-key emerge-basic-keymap "da" 'emerge-default-A)
331 (define-key emerge-basic-keymap "db" 'emerge-default-B)
332 (define-key emerge-basic-keymap "c" nil)
333 (define-key emerge-basic-keymap "ca" 'emerge-copy-as-kill-A)
334 (define-key emerge-basic-keymap "cb" 'emerge-copy-as-kill-B)
335 (define-key emerge-basic-keymap "i" nil)
336 (define-key emerge-basic-keymap "ia" 'emerge-insert-A)
337 (define-key emerge-basic-keymap "ib" 'emerge-insert-B)
338 (define-key emerge-basic-keymap "m" 'emerge-mark-difference)
339 (define-key emerge-basic-keymap "v" 'emerge-scroll-up)
340 (define-key emerge-basic-keymap "^" 'emerge-scroll-down)
341 (define-key emerge-basic-keymap "<" 'emerge-scroll-left)
342 (define-key emerge-basic-keymap ">" 'emerge-scroll-right)
343 (define-key emerge-basic-keymap "|" 'emerge-scroll-reset)
344 (define-key emerge-basic-keymap "x" nil)
345 (define-key emerge-basic-keymap "x1" 'emerge-one-line-window)
3b4a6e27
JB
346 (define-key emerge-basic-keymap "xc" 'emerge-combine-versions)
347 (define-key emerge-basic-keymap "xC" 'emerge-combine-versions-register)
3b4a6e27
JB
348 (define-key emerge-basic-keymap "xf" 'emerge-file-names)
349 (define-key emerge-basic-keymap "xj" 'emerge-join-differences)
350 (define-key emerge-basic-keymap "xl" 'emerge-line-numbers)
351 (define-key emerge-basic-keymap "xm" 'emerge-set-merge-mode)
352 (define-key emerge-basic-keymap "xs" 'emerge-split-difference)
353 (define-key emerge-basic-keymap "xt" 'emerge-trim-difference)
354 (define-key emerge-basic-keymap "xx" 'emerge-set-combine-versions-template)
355 ;; Allow emerge-basic-keymap to be referenced indirectly
356 (fset 'emerge-basic-keymap emerge-basic-keymap)
357 ;; Set up the fast mode keymap
358 (setq emerge-fast-keymap (copy-keymap emerge-basic-keymap))
359 ;; Allow prefixed commands to work in fast mode
360 (define-key emerge-fast-keymap emerge-command-prefix 'emerge-basic-keymap)
361 ;; Allow emerge-fast-keymap to be referenced indirectly
362 (fset 'emerge-fast-keymap emerge-fast-keymap)
363 ;; Suppress write-file and save-buffer
96473b34
AS
364 (define-key emerge-fast-keymap [remap write-file] 'emerge-query-write-file)
365 (define-key emerge-fast-keymap [remap save-buffer] 'emerge-query-save-buffer)
ff5f6ddd
RS
366
367 (define-key emerge-basic-keymap [menu-bar] (make-sparse-keymap))
368
623a8830
GM
369 (define-key emerge-fast-keymap [menu-bar emerge-options]
370 (cons "Merge-Options" emerge-options-menu))
ff5f6ddd
RS
371 (define-key emerge-fast-keymap [menu-bar merge]
372 (cons "Merge" emerge-merge-menu))
373 (define-key emerge-fast-keymap [menu-bar move]
374 (cons "Move" emerge-move-menu))
375
376 (define-key emerge-move-menu [emerge-scroll-reset]
377 '("Scroll Reset" . emerge-scroll-reset))
378 (define-key emerge-move-menu [emerge-scroll-right]
379 '("Scroll Right" . emerge-scroll-right))
380 (define-key emerge-move-menu [emerge-scroll-left]
381 '("Scroll Left" . emerge-scroll-left))
382 (define-key emerge-move-menu [emerge-scroll-down]
383 '("Scroll Down" . emerge-scroll-down))
384 (define-key emerge-move-menu [emerge-scroll-up]
385 '("Scroll Up" . emerge-scroll-up))
386 (define-key emerge-move-menu [emerge-recenter]
387 '("Recenter" . emerge-recenter))
388 (define-key emerge-move-menu [emerge-mark-difference]
389 '("Mark Difference" . emerge-mark-difference))
390 (define-key emerge-move-menu [emerge-jump-to-difference]
391 '("Jump To Difference" . emerge-jump-to-difference))
392 (define-key emerge-move-menu [emerge-find-difference]
393 '("Find Difference" . emerge-find-difference))
394 (define-key emerge-move-menu [emerge-previous-difference]
395 '("Previous Difference" . emerge-previous-difference))
396 (define-key emerge-move-menu [emerge-next-difference]
397 '("Next Difference" . emerge-next-difference))
398
399
400 (define-key emerge-options-menu [emerge-one-line-window]
401 '("One Line Window" . emerge-one-line-window))
402 (define-key emerge-options-menu [emerge-set-merge-mode]
623a8830 403 '("Set Merge Mode..." . emerge-set-merge-mode))
ff5f6ddd
RS
404 (define-key emerge-options-menu [emerge-set-combine-template]
405 '("Set Combine Template..." . emerge-set-combine-template))
406 (define-key emerge-options-menu [emerge-default-B]
407 '("Default B" . emerge-default-B))
408 (define-key emerge-options-menu [emerge-default-A]
409 '("Default A" . emerge-default-A))
410 (define-key emerge-options-menu [emerge-skip-prefers]
623a8830
GM
411 '(menu-item "Skip Prefers" emerge-skip-prefers
412 :button (:toggle . emerge-skip-prefers)))
ff5f6ddd 413 (define-key emerge-options-menu [emerge-auto-advance]
623a8830
GM
414 '(menu-item "Auto Advance" emerge-auto-advance
415 :button (:toggle . emerge-auto-advance)))
ff5f6ddd 416 (define-key emerge-options-menu [emerge-edit-mode]
623a8830 417 '(menu-item "Edit Mode" emerge-edit-mode :enable (not emerge-edit-mode)))
ff5f6ddd 418 (define-key emerge-options-menu [emerge-fast-mode]
623a8830 419 '(menu-item "Fast Mode" emerge-fast-mode :enable (not emerge-fast-mode)))
ff5f6ddd
RS
420
421 (define-key emerge-merge-menu [emerge-abort] '("Abort" . emerge-abort))
422 (define-key emerge-merge-menu [emerge-quit] '("Quit" . emerge-quit))
423 (define-key emerge-merge-menu [emerge-split-difference]
424 '("Split Difference" . emerge-split-difference))
425 (define-key emerge-merge-menu [emerge-join-differences]
426 '("Join Differences" . emerge-join-differences))
427 (define-key emerge-merge-menu [emerge-trim-difference]
428 '("Trim Difference" . emerge-trim-difference))
429 (define-key emerge-merge-menu [emerge-combine-versions]
430 '("Combine Versions" . emerge-combine-versions))
431 (define-key emerge-merge-menu [emerge-copy-as-kill-B]
432 '("Copy B as Kill" . emerge-copy-as-kill-B))
433 (define-key emerge-merge-menu [emerge-copy-as-kill-A]
434 '("Copy A as Kill" . emerge-copy-as-kill-A))
435 (define-key emerge-merge-menu [emerge-insert-B]
436 '("Insert B" . emerge-insert-B))
437 (define-key emerge-merge-menu [emerge-insert-A]
438 '("Insert A" . emerge-insert-A))
439 (define-key emerge-merge-menu [emerge-select-B]
440 '("Select B" . emerge-select-B))
441 (define-key emerge-merge-menu [emerge-select-A]
442 '("Select A" . emerge-select-A)))
443
3b4a6e27
JB
444
445;; Variables which control each merge. They are local to the merge buffer.
446
447;; Mode variables
448(emerge-defvar-local emerge-mode nil
449 "Indicator for emerge-mode.")
450(emerge-defvar-local emerge-fast-mode nil
451 "Indicator for emerge-mode fast submode.")
452(emerge-defvar-local emerge-edit-mode nil
453 "Indicator for emerge-mode edit submode.")
454(emerge-defvar-local emerge-A-buffer nil
455 "The buffer in which the A variant is stored.")
456(emerge-defvar-local emerge-B-buffer nil
457 "The buffer in which the B variant is stored.")
458(emerge-defvar-local emerge-merge-buffer nil
459 "The buffer in which the merged file is manipulated.")
460(emerge-defvar-local emerge-ancestor-buffer nil
461 "The buffer in which the ancestor variant is stored,
462or nil if there is none.")
463
464(defconst emerge-saved-variables
465 '((buffer-modified-p set-buffer-modified-p)
466 buffer-read-only
467 buffer-auto-save-file-name)
468 "Variables and properties of a buffer which are saved, modified and restored
469during a merge.")
470(defconst emerge-merging-values '(nil t nil)
471 "Values to be assigned to emerge-saved-variables during a merge.")
472
473(emerge-defvar-local emerge-A-buffer-values nil
474 "Remembers emerge-saved-variables for emerge-A-buffer.")
475(emerge-defvar-local emerge-B-buffer-values nil
476 "Remembers emerge-saved-variables for emerge-B-buffer.")
477
478(emerge-defvar-local emerge-difference-list nil
479 "Vector of differences between the variants, and markers in the buffers to
480show where they are. Each difference is represented by a vector of seven
481elements. The first two are markers to the beginning and end of the difference
482section in the A buffer, the second two are markers for the B buffer, the third
483two are markers for the merge buffer, and the last element is the \"state\" of
484that difference in the merge buffer.
485 A section of a buffer is described by two markers, one to the beginning of
486the first line of the section, and one to the beginning of the first line
487after the section. (If the section is empty, both markers point to the same
488point.) If the section is part of the selected difference, then the markers
489are moved into the flags, so the user can edit the section without disturbing
490the markers.
491 The \"states\" are:
492 A the merge buffer currently contains the A variant
493 B the merge buffer currently contains the B variant
494 default-A the merge buffer contains the A variant by default,
495 but this difference hasn't been selected yet, so
496 change-default commands can alter it
497 default-B the merge buffer contains the B variant by default,
498 but this difference hasn't been selected yet, so
499 change-default commands can alter it
eb8c3be9 500 prefer-A in a three-file merge, the A variant is the preferred
3b4a6e27 501 choice
eb8c3be9 502 prefer-B in a three-file merge, the B variant is the preferred
3b4a6e27
JB
503 choice")
504(emerge-defvar-local emerge-current-difference -1
505 "The difference that is currently selected.")
506(emerge-defvar-local emerge-number-of-differences nil
507 "Number of differences found.")
508(emerge-defvar-local emerge-edit-keymap nil
509 "The local keymap for the merge buffer, with the emerge commands defined in
510it. Used to save the local keymap during fast mode, when the local keymap is
511replaced by emerge-fast-keymap.")
512(emerge-defvar-local emerge-old-keymap nil
513 "The original local keymap for the merge buffer.")
514(emerge-defvar-local emerge-auto-advance nil
fb7ada5f 515 "If non-nil, emerge-select-A and emerge-select-B automatically advance to
3b4a6e27
JB
516the next difference.")
517(emerge-defvar-local emerge-skip-prefers nil
fb7ada5f 518 "If non-nil, differences for which there is a preference are automatically
3b4a6e27 519skipped.")
737e3892 520(emerge-defvar-local emerge-quit-hook nil
3b4a6e27 521 "Hooks to run in the merge buffer after the merge has been finished.
737e3892 522`emerge-prefix-argument' will hold the prefix argument of the `emerge-quit'
3b4a6e27 523command.
737e3892 524This is *not* a user option, since Emerge uses it for its own processing.")
3b4a6e27 525(emerge-defvar-local emerge-output-description nil
737e3892 526 "Describes output destination of emerge, for `emerge-file-names'.")
3b4a6e27
JB
527
528;;; Setup functions for two-file mode.
529
530(defun emerge-files-internal (file-A file-B &optional startup-hooks quit-hooks
8a946354 531 output-file)
151e4b9c 532 (if (not (file-readable-p file-A))
737e3892 533 (error "File `%s' does not exist or is not readable" file-A))
151e4b9c 534 (if (not (file-readable-p file-B))
737e3892 535 (error "File `%s' does not exist or is not readable" file-B))
3b4a6e27
JB
536 (let ((buffer-A (find-file-noselect file-A))
537 (buffer-B (find-file-noselect file-B)))
151e4b9c
RS
538 ;; Record the directories of the files
539 (setq emerge-last-dir-A (file-name-directory file-A))
540 (setq emerge-last-dir-B (file-name-directory file-B))
541 (if output-file
542 (setq emerge-last-dir-output (file-name-directory output-file)))
3b4a6e27 543 ;; Make sure the entire files are seen, and they reflect what is on disk
b05fde66 544 (with-current-buffer
151e4b9c
RS
545 buffer-A
546 (widen)
737e3892
RS
547 (let ((temp (file-local-copy file-A)))
548 (if temp
549 (setq file-A temp
550 startup-hooks
8a946354 551 (cons `(lambda () (delete-file ,file-A))
737e3892 552 startup-hooks))
8a946354
SS
553 ;; Verify that the file matches the buffer
554 (emerge-verify-file-buffer))))
b05fde66 555 (with-current-buffer
151e4b9c
RS
556 buffer-B
557 (widen)
737e3892
RS
558 (let ((temp (file-local-copy file-B)))
559 (if temp
560 (setq file-B temp
561 startup-hooks
8a946354 562 (cons `(lambda () (delete-file ,file-B))
737e3892 563 startup-hooks))
8a946354
SS
564 ;; Verify that the file matches the buffer
565 (emerge-verify-file-buffer))))
3b4a6e27
JB
566 (emerge-setup buffer-A file-A buffer-B file-B startup-hooks quit-hooks
567 output-file)))
568
569;; Start up Emerge on two files
570(defun emerge-setup (buffer-A file-A buffer-B file-B startup-hooks quit-hooks
571 output-file)
572 (setq file-A (expand-file-name file-A))
573 (setq file-B (expand-file-name file-B))
574 (setq output-file (and output-file (expand-file-name output-file)))
575 (let* ((merge-buffer-name (emerge-unique-buffer-name "*merge" "*"))
576 ;; create the merge buffer from buffer A, so it inherits buffer A's
577 ;; default directory, etc.
b05fde66 578 (merge-buffer (with-current-buffer
3b4a6e27
JB
579 buffer-A
580 (get-buffer-create merge-buffer-name))))
b05fde66 581 (with-current-buffer
3b4a6e27
JB
582 merge-buffer
583 (emerge-copy-modes buffer-A)
584 (setq buffer-read-only nil)
585 (auto-save-mode 1)
586 (setq emerge-mode t)
587 (setq emerge-A-buffer buffer-A)
588 (setq emerge-B-buffer buffer-B)
589 (setq emerge-ancestor-buffer nil)
590 (setq emerge-merge-buffer merge-buffer)
591 (setq emerge-output-description
592 (if output-file
593 (concat "Output to file: " output-file)
594 (concat "Output to buffer: " (buffer-name merge-buffer))))
de3cc816 595 (save-excursion (insert-buffer-substring emerge-A-buffer))
3b4a6e27
JB
596 (emerge-set-keys)
597 (setq emerge-difference-list (emerge-make-diff-list file-A file-B))
598 (setq emerge-number-of-differences (length emerge-difference-list))
599 (setq emerge-current-difference -1)
737e3892 600 (setq emerge-quit-hook quit-hooks)
151e4b9c
RS
601 (emerge-remember-buffer-characteristics)
602 (emerge-handle-local-variables))
3b4a6e27 603 (emerge-setup-windows buffer-A buffer-B merge-buffer t)
b05fde66 604 (with-current-buffer merge-buffer
737e3892 605 (run-hooks 'startup-hooks 'emerge-startup-hook)
3b4a6e27
JB
606 (setq buffer-read-only t))))
607
608;; Generate the Emerge difference list between two files
609(defun emerge-make-diff-list (file-A file-B)
610 (setq emerge-diff-buffer (get-buffer-create "*emerge-diff*"))
b05fde66 611 (with-current-buffer
3b4a6e27
JB
612 emerge-diff-buffer
613 (erase-buffer)
614 (shell-command
615 (format "%s %s %s %s"
151e4b9c
RS
616 emerge-diff-program emerge-diff-options
617 (emerge-protect-metachars file-A)
618 (emerge-protect-metachars file-B))
3b4a6e27 619 t))
661d3230 620 (emerge-prepare-error-list emerge-diff-ok-lines-regexp)
3b4a6e27
JB
621 (emerge-convert-diffs-to-markers
622 emerge-A-buffer emerge-B-buffer emerge-merge-buffer
623 (emerge-extract-diffs emerge-diff-buffer)))
624
625(defun emerge-extract-diffs (diff-buffer)
626 (let (list)
b05fde66 627 (with-current-buffer
3b4a6e27
JB
628 diff-buffer
629 (goto-char (point-min))
630 (while (re-search-forward emerge-match-diff-line nil t)
027a4b6b
JB
631 (let* ((a-begin (string-to-number (buffer-substring (match-beginning 1)
632 (match-end 1))))
3b4a6e27
JB
633 (a-end (let ((b (match-beginning 3))
634 (e (match-end 3)))
635 (if b
027a4b6b 636 (string-to-number (buffer-substring b e))
3b4a6e27
JB
637 a-begin)))
638 (diff-type (buffer-substring (match-beginning 4) (match-end 4)))
027a4b6b
JB
639 (b-begin (string-to-number (buffer-substring (match-beginning 5)
640 (match-end 5))))
3b4a6e27
JB
641 (b-end (let ((b (match-beginning 7))
642 (e (match-end 7)))
643 (if b
027a4b6b 644 (string-to-number (buffer-substring b e))
3b4a6e27
JB
645 b-begin))))
646 ;; fix the beginning and end numbers, because diff is somewhat
647 ;; strange about how it numbers lines
648 (if (string-equal diff-type "a")
649 (progn
650 (setq b-end (1+ b-end))
651 (setq a-begin (1+ a-begin))
652 (setq a-end a-begin))
653 (if (string-equal diff-type "d")
654 (progn
655 (setq a-end (1+ a-end))
656 (setq b-begin (1+ b-begin))
657 (setq b-end b-begin))
658 ;; (string-equal diff-type "c")
659 (progn
660 (setq a-end (1+ a-end))
661 (setq b-end (1+ b-end)))))
662 (setq list (cons (vector a-begin a-end
663 b-begin b-end
664 'default-A)
665 list)))))
666 (nreverse list)))
667
668;; Set up buffer of diff/diff3 error messages.
669(defun emerge-prepare-error-list (ok-regexp)
670 (setq emerge-diff-error-buffer (get-buffer-create "*emerge-diff-errors*"))
b05fde66 671 (with-current-buffer
3b4a6e27
JB
672 emerge-diff-error-buffer
673 (erase-buffer)
de3cc816 674 (save-excursion (insert-buffer-substring emerge-diff-buffer))
3b4a6e27
JB
675 (delete-matching-lines ok-regexp)))
676
677;;; Top-level and setup functions for three-file mode.
678
679(defun emerge-files-with-ancestor-internal (file-A file-B file-ancestor
737e3892
RS
680 &optional startup-hooks quit-hooks
681 output-file)
151e4b9c 682 (if (not (file-readable-p file-A))
737e3892 683 (error "File `%s' does not exist or is not readable" file-A))
151e4b9c 684 (if (not (file-readable-p file-B))
737e3892 685 (error "File `%s' does not exist or is not readable" file-B))
151e4b9c 686 (if (not (file-readable-p file-ancestor))
737e3892 687 (error "File `%s' does not exist or is not readable" file-ancestor))
3b4a6e27
JB
688 (let ((buffer-A (find-file-noselect file-A))
689 (buffer-B (find-file-noselect file-B))
690 (buffer-ancestor (find-file-noselect file-ancestor)))
151e4b9c
RS
691 ;; Record the directories of the files
692 (setq emerge-last-dir-A (file-name-directory file-A))
693 (setq emerge-last-dir-B (file-name-directory file-B))
694 (setq emerge-last-dir-ancestor (file-name-directory file-ancestor))
695 (if output-file
696 (setq emerge-last-dir-output (file-name-directory output-file)))
3b4a6e27 697 ;; Make sure the entire files are seen, and they reflect what is on disk
b05fde66 698 (with-current-buffer
151e4b9c
RS
699 buffer-A
700 (widen)
737e3892
RS
701 (let ((temp (file-local-copy file-A)))
702 (if temp
703 (setq file-A temp
704 startup-hooks
8a946354 705 (cons `(lambda () (delete-file ,file-A))
737e3892 706 startup-hooks))
8a946354
SS
707 ;; Verify that the file matches the buffer
708 (emerge-verify-file-buffer))))
b05fde66 709 (with-current-buffer
151e4b9c
RS
710 buffer-B
711 (widen)
737e3892
RS
712 (let ((temp (file-local-copy file-B)))
713 (if temp
714 (setq file-B temp
715 startup-hooks
8a946354 716 (cons `(lambda () (delete-file ,file-B))
737e3892 717 startup-hooks))
8a946354
SS
718 ;; Verify that the file matches the buffer
719 (emerge-verify-file-buffer))))
b05fde66 720 (with-current-buffer
151e4b9c
RS
721 buffer-ancestor
722 (widen)
737e3892
RS
723 (let ((temp (file-local-copy file-ancestor)))
724 (if temp
725 (setq file-ancestor temp
726 startup-hooks
8a946354 727 (cons `(lambda () (delete-file ,file-ancestor))
737e3892 728 startup-hooks))
8a946354
SS
729 ;; Verify that the file matches the buffer
730 (emerge-verify-file-buffer))))
3b4a6e27
JB
731 (emerge-setup-with-ancestor buffer-A file-A buffer-B file-B
732 buffer-ancestor file-ancestor
733 startup-hooks quit-hooks output-file)))
734
735;; Start up Emerge on two files with an ancestor
736(defun emerge-setup-with-ancestor (buffer-A file-A buffer-B file-B
737 buffer-ancestor file-ancestor
738 &optional startup-hooks quit-hooks
739 output-file)
740 (setq file-A (expand-file-name file-A))
741 (setq file-B (expand-file-name file-B))
742 (setq file-ancestor (expand-file-name file-ancestor))
743 (setq output-file (and output-file (expand-file-name output-file)))
744 (let* ((merge-buffer-name (emerge-unique-buffer-name "*merge" "*"))
745 ;; create the merge buffer from buffer A, so it inherits buffer A's
746 ;; default directory, etc.
b05fde66 747 (merge-buffer (with-current-buffer
3b4a6e27
JB
748 buffer-A
749 (get-buffer-create merge-buffer-name))))
b05fde66 750 (with-current-buffer
3b4a6e27
JB
751 merge-buffer
752 (emerge-copy-modes buffer-A)
753 (setq buffer-read-only nil)
754 (auto-save-mode 1)
755 (setq emerge-mode t)
756 (setq emerge-A-buffer buffer-A)
757 (setq emerge-B-buffer buffer-B)
758 (setq emerge-ancestor-buffer buffer-ancestor)
759 (setq emerge-merge-buffer merge-buffer)
760 (setq emerge-output-description
761 (if output-file
762 (concat "Output to file: " output-file)
763 (concat "Output to buffer: " (buffer-name merge-buffer))))
de3cc816 764 (save-excursion (insert-buffer-substring emerge-A-buffer))
3b4a6e27
JB
765 (emerge-set-keys)
766 (setq emerge-difference-list
767 (emerge-make-diff3-list file-A file-B file-ancestor))
768 (setq emerge-number-of-differences (length emerge-difference-list))
769 (setq emerge-current-difference -1)
737e3892 770 (setq emerge-quit-hook quit-hooks)
3b4a6e27 771 (emerge-remember-buffer-characteristics)
151e4b9c
RS
772 (emerge-select-prefer-Bs)
773 (emerge-handle-local-variables))
3b4a6e27 774 (emerge-setup-windows buffer-A buffer-B merge-buffer t)
b05fde66 775 (with-current-buffer merge-buffer
737e3892 776 (run-hooks 'startup-hooks 'emerge-startup-hook)
3b4a6e27
JB
777 (setq buffer-read-only t))))
778
779;; Generate the Emerge difference list between two files with an ancestor
780(defun emerge-make-diff3-list (file-A file-B file-ancestor)
781 (setq emerge-diff-buffer (get-buffer-create "*emerge-diff*"))
b05fde66 782 (with-current-buffer
3b4a6e27
JB
783 emerge-diff-buffer
784 (erase-buffer)
785 (shell-command
786 (format "%s %s %s %s %s"
787 emerge-diff3-program emerge-diff-options
151e4b9c 788 (emerge-protect-metachars file-A)
4a6e3980 789 (emerge-protect-metachars file-ancestor)
151e4b9c 790 (emerge-protect-metachars file-B))
3b4a6e27 791 t))
661d3230 792 (emerge-prepare-error-list emerge-diff3-ok-lines-regexp)
3b4a6e27
JB
793 (emerge-convert-diffs-to-markers
794 emerge-A-buffer emerge-B-buffer emerge-merge-buffer
795 (emerge-extract-diffs3 emerge-diff-buffer)))
796
797(defun emerge-extract-diffs3 (diff-buffer)
798 (let (list)
b05fde66 799 (with-current-buffer
3b4a6e27
JB
800 diff-buffer
801 (while (re-search-forward "^====\\(.?\\)$" nil t)
802 ;; leave point after matched line
803 (beginning-of-line 2)
804 (let ((agreement (buffer-substring (match-beginning 1) (match-end 1))))
805 ;; if the A and B files are the same, ignore the difference
4a6e3980 806 (if (not (string-equal agreement "2"))
3b4a6e27 807 (setq list
71296446 808 (cons
4a6e3980 809 (let (group-1 group-3 pos)
737e3892 810 (setq pos (point))
4a6e3980 811 (setq group-1 (emerge-get-diff3-group "1"))
737e3892
RS
812 (goto-char pos)
813 (setq group-3 (emerge-get-diff3-group "3"))
4a6e3980 814 (vector (car group-1) (car (cdr group-1))
3b4a6e27 815 (car group-3) (car (cdr group-3))
4a6e3980 816 (cond ((string-equal agreement "1") 'prefer-A)
3b4a6e27
JB
817 ((string-equal agreement "3") 'prefer-B)
818 (t 'default-A))))
819 list))))))
820 (nreverse list)))
821
822(defun emerge-get-diff3-group (file)
823 ;; This save-excursion allows emerge-get-diff3-group to be called for the
824 ;; various groups of lines (1, 2, 3) in any order, and for the lines to
825 ;; appear in any order. The reason this is necessary is that Gnu diff3
826 ;; can produce the groups in the order 1, 2, 3 or 1, 3, 2.
827 (save-excursion
828 (re-search-forward
829 (concat "^" file ":\\([0-9]+\\)\\(,\\([0-9]+\\)\\)?\\([ac]\\)$"))
830 (beginning-of-line 2)
831 ;; treatment depends on whether it is an "a" group or a "c" group
832 (if (string-equal (buffer-substring (match-beginning 4) (match-end 4)) "c")
833 ;; it is a "c" group
834 (if (match-beginning 2)
835 ;; it has two numbers
027a4b6b 836 (list (string-to-number
3b4a6e27 837 (buffer-substring (match-beginning 1) (match-end 1)))
027a4b6b 838 (1+ (string-to-number
3b4a6e27
JB
839 (buffer-substring (match-beginning 3) (match-end 3)))))
840 ;; it has one number
027a4b6b 841 (let ((x (string-to-number
3b4a6e27
JB
842 (buffer-substring (match-beginning 1) (match-end 1)))))
843 (list x (1+ x))))
844 ;; it is an "a" group
027a4b6b 845 (let ((x (1+ (string-to-number
3b4a6e27
JB
846 (buffer-substring (match-beginning 1) (match-end 1))))))
847 (list x x)))))
848
849;;; Functions to start Emerge on files
850
737e3892 851;;;###autoload
3b4a6e27
JB
852(defun emerge-files (arg file-A file-B file-out &optional startup-hooks
853 quit-hooks)
854 "Run Emerge on two files."
855 (interactive
856 (let (f)
857 (list current-prefix-arg
151e4b9c 858 (setq f (emerge-read-file-name "File A to merge" emerge-last-dir-A
90371ec9
RS
859 nil nil t))
860 (emerge-read-file-name "File B to merge" emerge-last-dir-B nil f t)
3b4a6e27 861 (and current-prefix-arg
151e4b9c 862 (emerge-read-file-name "Output file" emerge-last-dir-output
90371ec9 863 f f nil)))))
43d3039d 864 (if file-out
8a946354 865 (add-hook 'quit-hooks `(lambda () (emerge-files-exit ,file-out))))
3b4a6e27
JB
866 (emerge-files-internal
867 file-A file-B startup-hooks
43d3039d 868 quit-hooks
3b4a6e27
JB
869 file-out))
870
737e3892 871;;;###autoload
3b4a6e27
JB
872(defun emerge-files-with-ancestor (arg file-A file-B file-ancestor file-out
873 &optional startup-hooks quit-hooks)
874 "Run Emerge on two files, giving another file as the ancestor."
875 (interactive
876 (let (f)
877 (list current-prefix-arg
151e4b9c 878 (setq f (emerge-read-file-name "File A to merge" emerge-last-dir-A
90371ec9
RS
879 nil nil t))
880 (emerge-read-file-name "File B to merge" emerge-last-dir-B nil f t)
151e4b9c 881 (emerge-read-file-name "Ancestor file" emerge-last-dir-ancestor
90371ec9 882 nil f t)
3b4a6e27 883 (and current-prefix-arg
151e4b9c 884 (emerge-read-file-name "Output file" emerge-last-dir-output
90371ec9 885 f f nil)))))
43d3039d 886 (if file-out
8a946354 887 (add-hook 'quit-hooks `(lambda () (emerge-files-exit ,file-out))))
3b4a6e27
JB
888 (emerge-files-with-ancestor-internal
889 file-A file-B file-ancestor startup-hooks
43d3039d 890 quit-hooks
3b4a6e27
JB
891 file-out))
892
893;; Write the merge buffer out in place of the file the A buffer is visiting.
894(defun emerge-files-exit (file-out)
895 ;; if merge was successful was given, save to disk
896 (if (not emerge-prefix-argument)
897 (emerge-write-and-delete file-out)))
898
899;;; Functions to start Emerge on buffers
900
737e3892 901;;;###autoload
3b4a6e27
JB
902(defun emerge-buffers (buffer-A buffer-B &optional startup-hooks quit-hooks)
903 "Run Emerge on two buffers."
904 (interactive "bBuffer A to merge: \nbBuffer B to merge: ")
905 (let ((emerge-file-A (emerge-make-temp-file "A"))
906 (emerge-file-B (emerge-make-temp-file "B")))
b05fde66 907 (with-current-buffer
3b4a6e27
JB
908 buffer-A
909 (write-region (point-min) (point-max) emerge-file-A nil 'no-message))
b05fde66 910 (with-current-buffer
3b4a6e27
JB
911 buffer-B
912 (write-region (point-min) (point-max) emerge-file-B nil 'no-message))
913 (emerge-setup (get-buffer buffer-A) emerge-file-A
914 (get-buffer buffer-B) emerge-file-B
8a946354
SS
915 (cons `(lambda ()
916 (delete-file ,emerge-file-A)
917 (delete-file ,emerge-file-B))
3b4a6e27
JB
918 startup-hooks)
919 quit-hooks
920 nil)))
921
737e3892 922;;;###autoload
3b4a6e27 923(defun emerge-buffers-with-ancestor (buffer-A buffer-B buffer-ancestor
8a946354
SS
924 &optional startup-hooks
925 quit-hooks)
3b4a6e27
JB
926 "Run Emerge on two buffers, giving another buffer as the ancestor."
927 (interactive
928 "bBuffer A to merge: \nbBuffer B to merge: \nbAncestor buffer: ")
929 (let ((emerge-file-A (emerge-make-temp-file "A"))
930 (emerge-file-B (emerge-make-temp-file "B"))
931 (emerge-file-ancestor (emerge-make-temp-file "anc")))
b05fde66 932 (with-current-buffer
3b4a6e27
JB
933 buffer-A
934 (write-region (point-min) (point-max) emerge-file-A nil 'no-message))
b05fde66 935 (with-current-buffer
3b4a6e27
JB
936 buffer-B
937 (write-region (point-min) (point-max) emerge-file-B nil 'no-message))
b05fde66 938 (with-current-buffer
3b4a6e27
JB
939 buffer-ancestor
940 (write-region (point-min) (point-max) emerge-file-ancestor nil
941 'no-message))
942 (emerge-setup-with-ancestor (get-buffer buffer-A) emerge-file-A
943 (get-buffer buffer-B) emerge-file-B
944 (get-buffer buffer-ancestor)
945 emerge-file-ancestor
8a946354
SS
946 (cons `(lambda ()
947 (delete-file ,emerge-file-A)
948 (delete-file ,emerge-file-B)
949 (delete-file
950 ,emerge-file-ancestor))
3b4a6e27
JB
951 startup-hooks)
952 quit-hooks
953 nil)))
954
955;;; Functions to start Emerge from the command line
956
737e3892 957;;;###autoload
3b4a6e27
JB
958(defun emerge-files-command ()
959 (let ((file-a (nth 0 command-line-args-left))
960 (file-b (nth 1 command-line-args-left))
961 (file-out (nth 2 command-line-args-left)))
962 (setq command-line-args-left (nthcdr 3 command-line-args-left))
963 (emerge-files-internal
964 file-a file-b nil
8a946354 965 (list `(lambda () (emerge-command-exit ,file-out))))))
3b4a6e27 966
737e3892 967;;;###autoload
3b4a6e27
JB
968(defun emerge-files-with-ancestor-command ()
969 (let (file-a file-b file-anc file-out)
970 ;; check for a -a flag, for filemerge compatibility
971 (if (string= (car command-line-args-left) "-a")
972 ;; arguments are "-a ancestor file-a file-b file-out"
973 (progn
974 (setq file-a (nth 2 command-line-args-left))
975 (setq file-b (nth 3 command-line-args-left))
976 (setq file-anc (nth 1 command-line-args-left))
977 (setq file-out (nth 4 command-line-args-left))
978 (setq command-line-args-left (nthcdr 5 command-line-args-left)))
8a946354
SS
979 ;; arguments are "file-a file-b ancestor file-out"
980 (setq file-a (nth 0 command-line-args-left))
981 (setq file-b (nth 1 command-line-args-left))
982 (setq file-anc (nth 2 command-line-args-left))
983 (setq file-out (nth 3 command-line-args-left))
984 (setq command-line-args-left (nthcdr 4 command-line-args-left)))
3b4a6e27
JB
985 (emerge-files-with-ancestor-internal
986 file-a file-b file-anc nil
8a946354 987 (list `(lambda () (emerge-command-exit ,file-out))))))
71296446 988
3b4a6e27
JB
989(defun emerge-command-exit (file-out)
990 (emerge-write-and-delete file-out)
991 (kill-emacs (if emerge-prefix-argument 1 0)))
992
993;;; Functions to start Emerge via remote request
994
737e3892 995;;;###autoload
3b4a6e27
JB
996(defun emerge-files-remote (file-a file-b file-out)
997 (setq emerge-file-out file-out)
998 (emerge-files-internal
999 file-a file-b nil
8a946354 1000 (list `(lambda () (emerge-remote-exit ,file-out ',emerge-exit-func)))
3b4a6e27
JB
1001 file-out)
1002 (throw 'client-wait nil))
1003
737e3892 1004;;;###autoload
3b4a6e27
JB
1005(defun emerge-files-with-ancestor-remote (file-a file-b file-anc file-out)
1006 (setq emerge-file-out file-out)
1007 (emerge-files-with-ancestor-internal
1008 file-a file-b file-anc nil
8a946354 1009 (list `(lambda () (emerge-remote-exit ,file-out ',emerge-exit-func)))
3b4a6e27
JB
1010 file-out)
1011 (throw 'client-wait nil))
1012
ff5f6ddd 1013(defun emerge-remote-exit (file-out emerge-exit-func)
3b4a6e27
JB
1014 (emerge-write-and-delete file-out)
1015 (kill-buffer emerge-merge-buffer)
ff5f6ddd 1016 (funcall emerge-exit-func (if emerge-prefix-argument 1 0)))
3b4a6e27 1017
151e4b9c
RS
1018;;; Functions to start Emerge on RCS versions
1019
9343c8ee 1020;;;###autoload
151e4b9c
RS
1021(defun emerge-revisions (arg file revision-A revision-B
1022 &optional startup-hooks quit-hooks)
1023 "Emerge two RCS revisions of a file."
1024 (interactive
1025 (list current-prefix-arg
1026 (read-file-name "File to merge: " nil nil 'confirm)
1027 (read-string "Revision A to merge: " emerge-last-revision-A)
1028 (read-string "Revision B to merge: " emerge-last-revision-B)))
1029 (setq emerge-last-revision-A revision-A
1030 emerge-last-revision-B revision-B)
1031 (emerge-revisions-internal
1032 file revision-A revision-B startup-hooks
1033 (if arg
8a946354
SS
1034 (cons `(lambda ()
1035 (shell-command
1036 ,(format "%s %s" emerge-rcs-ci-program file)))
151e4b9c 1037 quit-hooks)
8a946354 1038 quit-hooks)))
151e4b9c 1039
9343c8ee 1040;;;###autoload
151e4b9c 1041(defun emerge-revisions-with-ancestor (arg file revision-A
8a946354
SS
1042 revision-B ancestor
1043 &optional
1044 startup-hooks quit-hooks)
737e3892 1045 "Emerge two RCS revisions of a file, with another revision as ancestor."
151e4b9c
RS
1046 (interactive
1047 (list current-prefix-arg
1048 (read-file-name "File to merge: " nil nil 'confirm)
1049 (read-string "Revision A to merge: " emerge-last-revision-A)
1050 (read-string "Revision B to merge: " emerge-last-revision-B)
1051 (read-string "Ancestor: " emerge-last-revision-ancestor)))
1052 (setq emerge-last-revision-A revision-A
1053 emerge-last-revision-B revision-B
1054 emerge-last-revision-ancestor ancestor)
1055 (emerge-revision-with-ancestor-internal
1056 file revision-A revision-B ancestor startup-hooks
1057 (if arg
1058 (let ((cmd ))
8a946354
SS
1059 (cons `(lambda ()
1060 (shell-command
1061 ,(format "%s %s" emerge-rcs-ci-program file)))
151e4b9c 1062 quit-hooks))
8a946354 1063 quit-hooks)))
151e4b9c
RS
1064
1065(defun emerge-revisions-internal (file revision-A revision-B &optional
8a946354 1066 startup-hooks quit-hooks output-file)
151e4b9c
RS
1067 (let ((buffer-A (get-buffer-create (format "%s,%s" file revision-A)))
1068 (buffer-B (get-buffer-create (format "%s,%s" file revision-B)))
1069 (emerge-file-A (emerge-make-temp-file "A"))
1070 (emerge-file-B (emerge-make-temp-file "B")))
1071 ;; Get the revisions into buffers
b05fde66 1072 (with-current-buffer
151e4b9c
RS
1073 buffer-A
1074 (erase-buffer)
1075 (shell-command
1076 (format "%s -q -p%s %s" emerge-rcs-co-program revision-A file)
1077 t)
1078 (write-region (point-min) (point-max) emerge-file-A nil 'no-message)
1079 (set-buffer-modified-p nil))
b05fde66 1080 (with-current-buffer
151e4b9c
RS
1081 buffer-B
1082 (erase-buffer)
1083 (shell-command
1084 (format "%s -q -p%s %s" emerge-rcs-co-program revision-B file)
1085 t)
1086 (write-region (point-min) (point-max) emerge-file-B nil 'no-message)
1087 (set-buffer-modified-p nil))
1088 ;; Do the merge
1089 (emerge-setup buffer-A emerge-file-A
1090 buffer-B emerge-file-B
8a946354
SS
1091 (cons `(lambda ()
1092 (delete-file ,emerge-file-A)
1093 (delete-file ,emerge-file-B))
151e4b9c 1094 startup-hooks)
8a946354 1095 (cons `(lambda () (emerge-files-exit ,file))
151e4b9c
RS
1096 quit-hooks)
1097 nil)))
1098
1099(defun emerge-revision-with-ancestor-internal (file revision-A revision-B
8a946354
SS
1100 ancestor
1101 &optional startup-hooks
1102 quit-hooks output-file)
151e4b9c
RS
1103 (let ((buffer-A (get-buffer-create (format "%s,%s" file revision-A)))
1104 (buffer-B (get-buffer-create (format "%s,%s" file revision-B)))
1105 (buffer-ancestor (get-buffer-create (format "%s,%s" file ancestor)))
1106 (emerge-file-A (emerge-make-temp-file "A"))
1107 (emerge-file-B (emerge-make-temp-file "B"))
1108 (emerge-ancestor (emerge-make-temp-file "ancestor")))
1109 ;; Get the revisions into buffers
b05fde66 1110 (with-current-buffer
151e4b9c
RS
1111 buffer-A
1112 (erase-buffer)
1113 (shell-command
1114 (format "%s -q -p%s %s" emerge-rcs-co-program
1115 revision-A file)
1116 t)
1117 (write-region (point-min) (point-max) emerge-file-A nil 'no-message)
1118 (set-buffer-modified-p nil))
b05fde66 1119 (with-current-buffer
151e4b9c
RS
1120 buffer-B
1121 (erase-buffer)
1122 (shell-command
1123 (format "%s -q -p%s %s" emerge-rcs-co-program revision-B file)
1124 t)
1125 (write-region (point-min) (point-max) emerge-file-B nil 'no-message)
1126 (set-buffer-modified-p nil))
b05fde66 1127 (with-current-buffer
151e4b9c
RS
1128 buffer-ancestor
1129 (erase-buffer)
1130 (shell-command
1131 (format "%s -q -p%s %s" emerge-rcs-co-program ancestor file)
1132 t)
1133 (write-region (point-min) (point-max) emerge-ancestor nil 'no-message)
1134 (set-buffer-modified-p nil))
1135 ;; Do the merge
1136 (emerge-setup-with-ancestor
1137 buffer-A emerge-file-A buffer-B emerge-file-B
1138 buffer-ancestor emerge-ancestor
8a946354
SS
1139 (cons `(lambda ()
1140 (delete-file ,emerge-file-A)
1141 (delete-file ,emerge-file-B)
1142 (delete-file ,emerge-ancestor))
151e4b9c 1143 startup-hooks)
8a946354 1144 (cons `(lambda () (emerge-files-exit ,file))
151e4b9c
RS
1145 quit-hooks)
1146 output-file)))
1147
1148;;; Function to start Emerge based on a line in a file
1149
1150(defun emerge-execute-line ()
737e3892
RS
1151 "Run Emerge using files named in current text line.
1152Looks in that line for whitespace-separated entries of these forms:
151e4b9c
RS
1153 a=file1
1154 b=file2
1155 ancestor=file3
1156 output=file4
737e3892 1157to specify the files to use in Emerge.
151e4b9c 1158
737e3892 1159In addition, if only one of `a=file' or `b=file' is present, and `output=file'
151e4b9c 1160is present:
737e3892 1161If `emerge-execute-line-deletions' is non-nil and `ancestor=file' is present,
151e4b9c
RS
1162it is assumed that the file in question has been deleted, and it is
1163not copied to the output file.
1164Otherwise, the A or B file present is copied to the output file."
1165 (interactive)
1166 (let (file-A file-B file-ancestor file-out
1167 (case-fold-search t))
1168 ;; Stop if at end of buffer (even though we might be in a line, if
1169 ;; the line does not end with newline)
1170 (if (eobp)
1171 (error "At end of buffer"))
1172 ;; Go to the beginning of the line
1173 (beginning-of-line)
1174 ;; Skip any initial whitespace
1175 (if (looking-at "[ \t]*")
1176 (goto-char (match-end 0)))
1177 ;; Process the entire line
1178 (while (not (eolp))
1179 ;; Get the next entry
1180 (if (looking-at "\\([a-z]+\\)=\\([^ \t\n]+\\)[ \t]*")
1181 ;; Break apart the tab (before =) and the filename (after =)
1182 (let ((tag (downcase
1183 (buffer-substring (match-beginning 1) (match-end 1))))
1184 (file (buffer-substring (match-beginning 2) (match-end 2))))
1185 ;; Move point after the entry
1186 (goto-char (match-end 0))
1187 ;; Store the filename in the right variable
1188 (cond
8a946354
SS
1189 ((string-equal tag "a")
1190 (if file-A
1191 (error "This line has two `A' entries"))
1192 (setq file-A file))
1193 ((string-equal tag "b")
1194 (if file-B
1195 (error "This line has two `B' entries"))
1196 (setq file-B file))
1197 ((or (string-equal tag "anc") (string-equal tag "ancestor"))
1198 (if file-ancestor
1199 (error "This line has two `ancestor' entries"))
1200 (setq file-ancestor file))
1201 ((or (string-equal tag "out") (string-equal tag "output"))
1202 (if file-out
1203 (error "This line has two `output' entries"))
1204 (setq file-out file))
1205 (t
1206 (error "Unrecognized entry"))))
1207 ;; If the match on the entry pattern failed
1208 (error "Unparsable entry")))
151e4b9c
RS
1209 ;; Make sure that file-A and file-B are present
1210 (if (not (or (and file-A file-B) file-out))
737e3892 1211 (error "Must have both `A' and `B' entries"))
151e4b9c 1212 (if (not (or file-A file-B))
737e3892 1213 (error "Must have `A' or `B' entry"))
151e4b9c
RS
1214 ;; Go to the beginning of the next line, so next execution will use
1215 ;; next line in buffer.
1216 (beginning-of-line 2)
1217 ;; Execute the correct command
1218 (cond
8a946354
SS
1219 ;; Merge of two files with ancestor
1220 ((and file-A file-B file-ancestor)
1221 (message "Merging %s and %s..." file-A file-B)
1222 (emerge-files-with-ancestor (not (not file-out)) file-A file-B
1223 file-ancestor file-out
1224 nil
1225 ;; When done, return to this buffer.
1226 (list
1227 `(lambda ()
1228 (switch-to-buffer ,(current-buffer))
1229 (message "Merge done.")))))
1230 ;; Merge of two files without ancestor
1231 ((and file-A file-B)
1232 (message "Merging %s and %s..." file-A file-B)
1233 (emerge-files (not (not file-out)) file-A file-B file-out
1234 nil
1235 ;; When done, return to this buffer.
71296446 1236 (list
8a946354
SS
1237 `(lambda ()
1238 (switch-to-buffer ,(current-buffer))
1239 (message "Merge done.")))))
1240 ;; There is an output file (or there would have been an error above),
1241 ;; but only one input file.
1242 ;; The file appears to have been deleted in one version; do nothing.
1243 ((and file-ancestor emerge-execute-line-deletions)
1244 (message "No action."))
1245 ;; The file should be copied from the version that contains it
1246 (t (let ((input-file (or file-A file-B)))
1247 (message "Copying...")
1248 (copy-file input-file file-out)
1249 (message "%s copied to %s." input-file file-out))))))
151e4b9c
RS
1250
1251;;; Sample function for creating information for emerge-execute-line
1252
33933d45
AS
1253(defcustom emerge-merge-directories-filename-regexp "[^.]"
1254 "Regexp describing files to be processed by `emerge-merge-directories'."
1255 :type 'regexp
1256 :group 'emerge)
151e4b9c 1257
9343c8ee 1258;;;###autoload
151e4b9c 1259(defun emerge-merge-directories (a-dir b-dir ancestor-dir output-dir)
71296446 1260 (interactive
151e4b9c 1261 (list
7e27ce9c
AL
1262 (read-directory-name "A directory: " nil nil 'confirm)
1263 (read-directory-name "B directory: " nil nil 'confirm)
1264 (read-directory-name "Ancestor directory (null for none): " nil nil 'confirm)
1265 (read-directory-name "Output directory (null for none): " nil nil 'confirm)))
151e4b9c
RS
1266 ;; Check that we're not on a line
1267 (if (not (and (bolp) (eolp)))
1268 (error "There is text on this line"))
1269 ;; Turn null strings into nil to indicate directories not used.
1270 (if (and ancestor-dir (string-equal ancestor-dir ""))
1271 (setq ancestor-dir nil))
1272 (if (and output-dir (string-equal output-dir ""))
1273 (setq output-dir nil))
1274 ;; Canonicalize the directory names
1275 (setq a-dir (expand-file-name a-dir))
1276 (if (not (string-equal (substring a-dir -1) "/"))
1277 (setq a-dir (concat a-dir "/")))
1278 (setq b-dir (expand-file-name b-dir))
1279 (if (not (string-equal (substring b-dir -1) "/"))
1280 (setq b-dir (concat b-dir "/")))
1281 (if ancestor-dir
1282 (progn
1283 (setq ancestor-dir (expand-file-name ancestor-dir))
1284 (if (not (string-equal (substring ancestor-dir -1) "/"))
1285 (setq ancestor-dir (concat ancestor-dir "/")))))
1286 (if output-dir
1287 (progn
1288 (setq output-dir (expand-file-name output-dir))
1289 (if (not (string-equal (substring output-dir -1) "/"))
1290 (setq output-dir (concat output-dir "/")))))
1291 ;; Set the mark to where we start
1292 (push-mark)
1293 ;; Find out what files are in the directories.
1294 (let* ((a-dir-files
1295 (directory-files a-dir nil emerge-merge-directories-filename-regexp))
1296 (b-dir-files
1297 (directory-files b-dir nil emerge-merge-directories-filename-regexp))
1298 (ancestor-dir-files
1299 (and ancestor-dir
1300 (directory-files ancestor-dir nil
1301 emerge-merge-directories-filename-regexp)))
1302 (all-files (sort (nconc (copy-sequence a-dir-files)
1303 (copy-sequence b-dir-files)
1304 (copy-sequence ancestor-dir-files))
1305 (function string-lessp))))
1306 ;; Remove duplicates from all-files.
1307 (let ((p all-files))
1308 (while p
1309 (if (and (cdr p) (string-equal (car p) (car (cdr p))))
1310 (setcdr p (cdr (cdr p)))
1311 (setq p (cdr p)))))
1312 ;; Generate the control lines for the various files.
1313 (while all-files
1314 (let ((f (car all-files)))
1315 (setq all-files (cdr all-files))
1316 (if (and a-dir-files (string-equal (car a-dir-files) f))
1317 (progn
1318 (insert "A=" a-dir f "\t")
1319 (setq a-dir-files (cdr a-dir-files))))
1320 (if (and b-dir-files (string-equal (car b-dir-files) f))
1321 (progn
1322 (insert "B=" b-dir f "\t")
1323 (setq b-dir-files (cdr b-dir-files))))
1324 (if (and ancestor-dir-files (string-equal (car ancestor-dir-files) f))
1325 (progn
1326 (insert "ancestor=" ancestor-dir f "\t")
1327 (setq ancestor-dir-files (cdr ancestor-dir-files))))
1328 (if output-dir
1329 (insert "output=" output-dir f "\t"))
1330 (backward-delete-char 1)
1331 (insert "\n")))))
1332
3b4a6e27
JB
1333;;; Common setup routines
1334
1335;; Set up the window configuration. If POS is given, set the points to
1336;; the beginnings of the buffers.
1337(defun emerge-setup-windows (buffer-A buffer-B merge-buffer &optional pos)
1338 ;; Make sure we are not in the minibuffer window when we try to delete
1339 ;; all other windows.
1340 (if (eq (selected-window) (minibuffer-window))
1341 (other-window 1))
1342 (delete-other-windows)
1343 (switch-to-buffer merge-buffer)
1344 (emerge-refresh-mode-line)
2d197ffb
CY
1345 (split-window-below)
1346 (split-window-right)
3b4a6e27
JB
1347 (switch-to-buffer buffer-A)
1348 (if pos
1349 (goto-char (point-min)))
1350 (other-window 1)
1351 (switch-to-buffer buffer-B)
1352 (if pos
1353 (goto-char (point-min)))
1354 (other-window 1)
1355 (if pos
1356 (goto-char (point-min)))
1357 ;; If diff/diff3 reports errors, display them rather than the merge buffer.
b05fde66 1358 (if (/= 0 (with-current-buffer emerge-diff-error-buffer (buffer-size)))
3b4a6e27
JB
1359 (progn
1360 (ding)
1361 (message "Errors found in diff/diff3 output. Merge buffer is %s."
1362 (buffer-name emerge-merge-buffer))
1363 (switch-to-buffer emerge-diff-error-buffer))))
1364
1365;; Set up the keymap in the merge buffer
1366(defun emerge-set-keys ()
1367 ;; Set up fixed keymaps if necessary
1368 (if (not emerge-basic-keymap)
1369 (emerge-setup-fixed-keymaps))
1370 ;; Save the old local map
1371 (setq emerge-old-keymap (current-local-map))
1372 ;; Construct the edit keymap
1373 (setq emerge-edit-keymap (if emerge-old-keymap
1374 (copy-keymap emerge-old-keymap)
1375 (make-sparse-keymap)))
1376 ;; Install the Emerge commands
1377 (emerge-force-define-key emerge-edit-keymap emerge-command-prefix
1378 'emerge-basic-keymap)
ff5f6ddd
RS
1379 (define-key emerge-edit-keymap [menu-bar] (make-sparse-keymap))
1380
1381 ;; Create the additional menu bar items.
623a8830
GM
1382 (define-key emerge-edit-keymap [menu-bar emerge-options]
1383 (cons "Merge-Options" emerge-options-menu))
ff5f6ddd
RS
1384 (define-key emerge-edit-keymap [menu-bar merge]
1385 (cons "Merge" emerge-merge-menu))
1386 (define-key emerge-edit-keymap [menu-bar move]
1387 (cons "Move" emerge-move-menu))
1388
3b4a6e27 1389 ;; Suppress write-file and save-buffer
737e3892
RS
1390 (substitute-key-definition 'write-file
1391 'emerge-query-write-file
1392 emerge-edit-keymap)
1393 (substitute-key-definition 'save-buffer
1394 'emerge-query-save-buffer
1395 emerge-edit-keymap)
96473b34
AS
1396 (define-key emerge-edit-keymap [remap write-file] 'emerge-query-write-file)
1397 (define-key emerge-edit-keymap [remap save-buffer] 'emerge-query-save-buffer)
3b4a6e27
JB
1398 (use-local-map emerge-fast-keymap)
1399 (setq emerge-edit-mode nil)
1400 (setq emerge-fast-mode t))
1401
1402(defun emerge-remember-buffer-characteristics ()
737e3892
RS
1403 "Record certain properties of the buffers being merged.
1404Must be called in the merge buffer. Remembers read-only, modified,
1405auto-save, and saves them in buffer local variables. Sets the buffers
1406read-only and turns off `auto-save-mode'.
1407These characteristics are restored by `emerge-restore-buffer-characteristics'."
3b4a6e27
JB
1408 ;; force auto-save, because we will turn off auto-saving in buffers for the
1409 ;; duration
1410 (do-auto-save)
1411 ;; remember and alter buffer characteristics
1412 (setq emerge-A-buffer-values
b05fde66 1413 (with-current-buffer
3b4a6e27
JB
1414 emerge-A-buffer
1415 (prog1
1416 (emerge-save-variables emerge-saved-variables)
1417 (emerge-restore-variables emerge-saved-variables
1418 emerge-merging-values))))
1419 (setq emerge-B-buffer-values
b05fde66 1420 (with-current-buffer
3b4a6e27
JB
1421 emerge-B-buffer
1422 (prog1
1423 (emerge-save-variables emerge-saved-variables)
1424 (emerge-restore-variables emerge-saved-variables
1425 emerge-merging-values)))))
1426
1427(defun emerge-restore-buffer-characteristics ()
c9ec040a 1428 "Restore characteristics saved by `emerge-remember-buffer-characteristics'."
3b4a6e27
JB
1429 (let ((A-values emerge-A-buffer-values)
1430 (B-values emerge-B-buffer-values))
b05fde66 1431 (with-current-buffer emerge-A-buffer
3b4a6e27
JB
1432 (emerge-restore-variables emerge-saved-variables
1433 A-values))
b05fde66 1434 (with-current-buffer emerge-B-buffer
3b4a6e27
JB
1435 (emerge-restore-variables emerge-saved-variables
1436 B-values))))
1437
4d4cd289
RS
1438;; Move to line DESIRED-LINE assuming we are at line CURRENT-LINE.
1439;; Return DESIRED-LINE.
1440(defun emerge-goto-line (desired-line current-line)
1441 (forward-line (- desired-line current-line))
d9021674 1442 desired-line)
4d4cd289 1443
3b4a6e27
JB
1444(defun emerge-convert-diffs-to-markers (A-buffer
1445 B-buffer
1446 merge-buffer
1447 lineno-list)
1448 (let* (marker-list
b05fde66 1449 (A-point-min (with-current-buffer A-buffer (point-min)))
3b4a6e27 1450 (offset (1- A-point-min))
b05fde66 1451 (B-point-min (with-current-buffer B-buffer (point-min)))
4d4cd289
RS
1452 ;; Record current line number in each buffer
1453 ;; so we don't have to count from the beginning.
d9021674
KH
1454 (a-line 1)
1455 (b-line 1))
b05fde66
GM
1456 (with-current-buffer A-buffer (goto-char (point-min)))
1457 (with-current-buffer B-buffer (goto-char (point-min)))
3b4a6e27
JB
1458 (while lineno-list
1459 (let* ((list-element (car lineno-list))
1460 a-begin-marker
1461 a-end-marker
1462 b-begin-marker
1463 b-end-marker
151e4b9c
RS
1464 merge-begin-marker
1465 merge-end-marker
3b4a6e27
JB
1466 (a-begin (aref list-element 0))
1467 (a-end (aref list-element 1))
1468 (b-begin (aref list-element 2))
1469 (b-end (aref list-element 3))
1470 (state (aref list-element 4)))
1471 ;; place markers at the appropriate places in the buffers
b05fde66 1472 (with-current-buffer
3b4a6e27 1473 A-buffer
d9021674 1474 (setq a-line (emerge-goto-line a-begin a-line))
3b4a6e27 1475 (setq a-begin-marker (point-marker))
d9021674 1476 (setq a-line (emerge-goto-line a-end a-line))
3b4a6e27 1477 (setq a-end-marker (point-marker)))
b05fde66 1478 (with-current-buffer
3b4a6e27 1479 B-buffer
d9021674 1480 (setq b-line (emerge-goto-line b-begin b-line))
3b4a6e27 1481 (setq b-begin-marker (point-marker))
d9021674 1482 (setq b-line (emerge-goto-line b-end b-line))
3b4a6e27
JB
1483 (setq b-end-marker (point-marker)))
1484 (setq merge-begin-marker (set-marker
1485 (make-marker)
1486 (- (marker-position a-begin-marker)
1487 offset)
1488 merge-buffer))
1489 (setq merge-end-marker (set-marker
1490 (make-marker)
1491 (- (marker-position a-end-marker)
1492 offset)
1493 merge-buffer))
1494 ;; record all the markers for this difference
1495 (setq marker-list (cons (vector a-begin-marker a-end-marker
1496 b-begin-marker b-end-marker
1497 merge-begin-marker merge-end-marker
1498 state)
1499 marker-list)))
1500 (setq lineno-list (cdr lineno-list)))
1501 ;; convert the list of difference information into a vector for
1502 ;; fast access
1503 (setq emerge-difference-list (apply 'vector (nreverse marker-list)))))
1504
71296446 1505;; If we have an ancestor, select all B variants that we prefer
3b4a6e27
JB
1506(defun emerge-select-prefer-Bs ()
1507 (let ((n 0))
1508 (while (< n emerge-number-of-differences)
1509 (if (eq (aref (aref emerge-difference-list n) 6) 'prefer-B)
1510 (progn
1511 (emerge-unselect-and-select-difference n t)
1512 (emerge-select-B)
1513 (aset (aref emerge-difference-list n) 6 'prefer-B)))
1514 (setq n (1+ n))))
1515 (emerge-unselect-and-select-difference -1))
1516
151e4b9c
RS
1517;; Process the local-variables list at the end of the merged file, if
1518;; requested.
1519(defun emerge-handle-local-variables ()
1520 (if emerge-process-local-variables
1521 (condition-case err
4a6e3980 1522 (hack-local-variables)
151e4b9c
RS
1523 (error (message "Local-variables error in merge buffer: %s"
1524 (prin1-to-string err))))))
1525
3b4a6e27
JB
1526;;; Common exit routines
1527
1528(defun emerge-write-and-delete (file-out)
1529 ;; clear screen format
1530 (delete-other-windows)
1531 ;; delete A, B, and ancestor buffers, if they haven't been changed
1532 (if (not (buffer-modified-p emerge-A-buffer))
1533 (kill-buffer emerge-A-buffer))
1534 (if (not (buffer-modified-p emerge-B-buffer))
1535 (kill-buffer emerge-B-buffer))
1536 (if (and emerge-ancestor-buffer
1537 (not (buffer-modified-p emerge-ancestor-buffer)))
1538 (kill-buffer emerge-ancestor-buffer))
1539 ;; Write merge buffer to file
737e3892
RS
1540 (and file-out
1541 (write-file file-out)))
3b4a6e27
JB
1542
1543;;; Commands
1544
1545(defun emerge-recenter (&optional arg)
737e3892
RS
1546 "Bring the highlighted region of all three merge buffers into view.
1547This brings the buffers into view if they are in windows.
1548With an argument, reestablish the default three-window display."
3b4a6e27
JB
1549 (interactive "P")
1550 ;; If there is an argument, rebuild the window structure
1551 (if arg
1552 (emerge-setup-windows emerge-A-buffer emerge-B-buffer
1553 emerge-merge-buffer))
1554 ;; Redisplay whatever buffers are showing, if there is a selected difference
1555 (if (and (>= emerge-current-difference 0)
1556 (< emerge-current-difference emerge-number-of-differences))
1557 (let* ((merge-buffer emerge-merge-buffer)
1558 (buffer-A emerge-A-buffer)
1559 (buffer-B emerge-B-buffer)
f405e887
RS
1560 (window-A (get-buffer-window buffer-A 'visible))
1561 (window-B (get-buffer-window buffer-B 'visible))
3b4a6e27
JB
1562 (merge-window (get-buffer-window merge-buffer))
1563 (diff-vector
1564 (aref emerge-difference-list emerge-current-difference)))
1565 (if window-A (progn
1566 (select-window window-A)
1567 (emerge-position-region
1568 (- (aref diff-vector 0)
1569 (1- emerge-before-flag-length))
1570 (+ (aref diff-vector 1)
1571 (1- emerge-after-flag-length))
1572 (1+ (aref diff-vector 0)))))
1573 (if window-B (progn
1574 (select-window window-B)
1575 (emerge-position-region
1576 (- (aref diff-vector 2)
1577 (1- emerge-before-flag-length))
1578 (+ (aref diff-vector 3)
1579 (1- emerge-after-flag-length))
1580 (1+ (aref diff-vector 2)))))
1581 (if merge-window (progn
1582 (select-window merge-window)
1583 (emerge-position-region
1584 (- (aref diff-vector 4)
1585 (1- emerge-before-flag-length))
1586 (+ (aref diff-vector 5)
1587 (1- emerge-after-flag-length))
1588 (1+ (aref diff-vector 4))))))))
1589
1590;;; Window scrolling operations
1591;; These operations are designed to scroll all three windows the same amount,
1592;; so as to keep the text in them aligned.
1593
1594;; Perform some operation on all three windows (if they are showing).
1595;; Catches all errors on the operation in the A and B windows, but not
1596;; in the merge window. Usually, errors come from scrolling off the
1597;; beginning or end of the buffer, and this gives a nice error message:
1598;; End of buffer is reported in the merge buffer, but if the scroll was
1599;; possible in the A or B windows, it is performed there before the error
1600;; is reported.
1601(defun emerge-operate-on-windows (operation arg)
1602 (let* ((merge-buffer emerge-merge-buffer)
1603 (buffer-A emerge-A-buffer)
1604 (buffer-B emerge-B-buffer)
f405e887
RS
1605 (window-A (get-buffer-window buffer-A 'visible))
1606 (window-B (get-buffer-window buffer-B 'visible))
3b4a6e27
JB
1607 (merge-window (get-buffer-window merge-buffer)))
1608 (if window-A (progn
1609 (select-window window-A)
1610 (condition-case nil
1611 (funcall operation arg)
1612 (error))))
1613 (if window-B (progn
1614 (select-window window-B)
1615 (condition-case nil
1616 (funcall operation arg)
1617 (error))))
1618 (if merge-window (progn
1619 (select-window merge-window)
1620 (funcall operation arg)))))
1621
1622(defun emerge-scroll-up (&optional arg)
1623 "Scroll up all three merge buffers, if they are in windows.
737e3892
RS
1624With argument N, scroll N lines; otherwise scroll by nearly
1625the height of the merge window.
1626`C-u -' alone as argument scrolls half the height of the merge window."
3b4a6e27
JB
1627 (interactive "P")
1628 (emerge-operate-on-windows
71296446 1629 'scroll-up
3b4a6e27
JB
1630 ;; calculate argument to scroll-up
1631 ;; if there is an explicit argument
1632 (if (and arg (not (equal arg '-)))
1633 ;; use it
1634 (prefix-numeric-value arg)
1635 ;; if not, see if we can determine a default amount (the window height)
1636 (let ((merge-window (get-buffer-window emerge-merge-buffer)))
1637 (if (null merge-window)
1638 ;; no window, use nil
1639 nil
1640 (let ((default-amount
1641 (- (window-height merge-window) 1 next-screen-context-lines)))
1642 ;; the window was found
1643 (if arg
1644 ;; C-u as argument means half of default amount
1645 (/ default-amount 2)
1646 ;; no argument means default amount
1647 default-amount)))))))
1648
1649(defun emerge-scroll-down (&optional arg)
1650 "Scroll down all three merge buffers, if they are in windows.
737e3892
RS
1651With argument N, scroll N lines; otherwise scroll by nearly
1652the height of the merge window.
1653`C-u -' alone as argument scrolls half the height of the merge window."
3b4a6e27
JB
1654 (interactive "P")
1655 (emerge-operate-on-windows
1656 'scroll-down
1657 ;; calculate argument to scroll-down
1658 ;; if there is an explicit argument
1659 (if (and arg (not (equal arg '-)))
1660 ;; use it
1661 (prefix-numeric-value arg)
1662 ;; if not, see if we can determine a default amount (the window height)
1663 (let ((merge-window (get-buffer-window emerge-merge-buffer)))
1664 (if (null merge-window)
1665 ;; no window, use nil
1666 nil
1667 (let ((default-amount
1668 (- (window-height merge-window) 1 next-screen-context-lines)))
1669 ;; the window was found
1670 (if arg
1671 ;; C-u as argument means half of default amount
1672 (/ default-amount 2)
1673 ;; no argument means default amount
1674 default-amount)))))))
1675
1676(defun emerge-scroll-left (&optional arg)
1677 "Scroll left all three merge buffers, if they are in windows.
737e3892 1678If an argument is given, that is how many columns are scrolled, else nearly
151e4b9c 1679the width of the A and B windows. `C-u -' alone as argument scrolls half the
3b4a6e27
JB
1680width of the A and B windows."
1681 (interactive "P")
1682 (emerge-operate-on-windows
1683 'scroll-left
1684 ;; calculate argument to scroll-left
1685 ;; if there is an explicit argument
1686 (if (and arg (not (equal arg '-)))
1687 ;; use it
1688 (prefix-numeric-value arg)
1689 ;; if not, see if we can determine a default amount
1690 ;; (half the window width)
1691 (let ((merge-window (get-buffer-window emerge-merge-buffer)))
1692 (if (null merge-window)
1693 ;; no window, use nil
1694 nil
1695 (let ((default-amount
1696 (- (/ (window-width merge-window) 2) 3)))
1697 ;; the window was found
1698 (if arg
1699 ;; C-u as argument means half of default amount
1700 (/ default-amount 2)
1701 ;; no argument means default amount
1702 default-amount)))))))
1703
1704(defun emerge-scroll-right (&optional arg)
1705 "Scroll right all three merge buffers, if they are in windows.
737e3892 1706If an argument is given, that is how many columns are scrolled, else nearly
151e4b9c 1707the width of the A and B windows. `C-u -' alone as argument scrolls half the
3b4a6e27
JB
1708width of the A and B windows."
1709 (interactive "P")
1710 (emerge-operate-on-windows
1711 'scroll-right
1712 ;; calculate argument to scroll-right
1713 ;; if there is an explicit argument
1714 (if (and arg (not (equal arg '-)))
1715 ;; use it
1716 (prefix-numeric-value arg)
1717 ;; if not, see if we can determine a default amount
1718 ;; (half the window width)
1719 (let ((merge-window (get-buffer-window emerge-merge-buffer)))
1720 (if (null merge-window)
1721 ;; no window, use nil
1722 nil
1723 (let ((default-amount
1724 (- (/ (window-width merge-window) 2) 3)))
1725 ;; the window was found
1726 (if arg
1727 ;; C-u as argument means half of default amount
1728 (/ default-amount 2)
1729 ;; no argument means default amount
1730 default-amount)))))))
1731
1732(defun emerge-scroll-reset ()
737e3892
RS
1733 "Reset horizontal scrolling in Emerge.
1734This resets the horizontal scrolling of all three merge buffers
1735to the left margin, if they are in windows."
3b4a6e27
JB
1736 (interactive)
1737 (emerge-operate-on-windows
b05fde66 1738 (lambda (x) (set-window-hscroll (selected-window) 0))
3b4a6e27
JB
1739 nil))
1740
1741;; Attempt to show the region nicely.
1742;; If there are min-lines lines above and below the region, then don't do
1743;; anything.
1744;; If not, recenter the region to make it so.
301b181a 1745;; If that isn't possible, remove context lines evenly from top and bottom
3b4a6e27
JB
1746;; so the entire region shows.
1747;; If that isn't possible, show the top of the region.
1748;; BEG must be at the beginning of a line.
1749(defun emerge-position-region (beg end pos)
1750 ;; First test whether the entire region is visible with
1751 ;; emerge-min-visible-lines above and below it
1752 (if (not (and (<= (progn
1753 (move-to-window-line emerge-min-visible-lines)
1754 (point))
1755 beg)
1756 (<= end (progn
1757 (move-to-window-line
1758 (- (1+ emerge-min-visible-lines)))
1759 (point)))))
1760 ;; We failed that test, see if it fits at all
1761 ;; Meanwhile positioning it correctly in case it doesn't fit
1762 (progn
1763 (set-window-start (selected-window) beg)
151e4b9c 1764 (if (pos-visible-in-window-p end)
3b4a6e27
JB
1765 ;; Determine the number of lines that the region occupies
1766 (let ((lines 0))
1767 (while (> end (progn
1768 (move-to-window-line lines)
1769 (point)))
1770 (setq lines (1+ lines)))
1771 ;; And position the beginning on the right line
1772 (goto-char beg)
1773 (recenter (/ (1+ (- (1- (window-height (selected-window)))
1774 lines))
1775 2))))))
1776 (goto-char pos))
1777
1778(defun emerge-next-difference ()
1779 "Advance to the next difference."
1780 (interactive)
1781 (if (< emerge-current-difference emerge-number-of-differences)
1782 (let ((n (1+ emerge-current-difference)))
1783 (while (and emerge-skip-prefers
1784 (< n emerge-number-of-differences)
1785 (memq (aref (aref emerge-difference-list n) 6)
1786 '(prefer-A prefer-B)))
1787 (setq n (1+ n)))
1788 (let ((buffer-read-only nil))
1789 (emerge-unselect-and-select-difference n)))
1790 (error "At end")))
1791
1792(defun emerge-previous-difference ()
1793 "Go to the previous difference."
1794 (interactive)
1795 (if (> emerge-current-difference -1)
1796 (let ((n (1- emerge-current-difference)))
1797 (while (and emerge-skip-prefers
1798 (> n -1)
1799 (memq (aref (aref emerge-difference-list n) 6)
1800 '(prefer-A prefer-B)))
1801 (setq n (1- n)))
1802 (let ((buffer-read-only nil))
1803 (emerge-unselect-and-select-difference n)))
1804 (error "At beginning")))
1805
1806(defun emerge-jump-to-difference (difference-number)
1807 "Go to the N-th difference."
1808 (interactive "p")
1809 (let ((buffer-read-only nil))
1810 (setq difference-number (1- difference-number))
1811 (if (and (>= difference-number -1)
1812 (< difference-number (1+ emerge-number-of-differences)))
1813 (emerge-unselect-and-select-difference difference-number)
1814 (error "Bad difference number"))))
1815
737e3892
RS
1816(defun emerge-abort ()
1817 "Abort the Emerge session."
1818 (interactive)
1819 (emerge-quit t))
1820
3b4a6e27 1821(defun emerge-quit (arg)
737e3892
RS
1822 "Finish the Emerge session and exit Emerge.
1823Prefix argument means to abort rather than successfully finish.
1824The difference depends on how the merge was started,
3b4a6e27
JB
1825but usually means to not write over one of the original files, or to signal
1826to some process which invoked Emerge a failure code.
1827
1828Unselects the selected difference, if any, restores the read-only and modified
1829flags of the merged file buffers, restores the local keymap of the merge
1830buffer, and sets off various emerge flags. Using Emerge commands in this
1831buffer after this will cause serious problems."
1832 (interactive "P")
1833 (if (prog1
1834 (y-or-n-p
1835 (if (not arg)
1836 "Do you really want to successfully finish this merge? "
1837 "Do you really want to abort this merge? "))
1838 (message ""))
1839 (emerge-really-quit arg)))
1840
1841;; Perform the quit operations.
1842(defun emerge-really-quit (arg)
1843 (setq buffer-read-only nil)
1844 (emerge-unselect-and-select-difference -1)
1845 (emerge-restore-buffer-characteristics)
1846 ;; null out the difference markers so they don't slow down future editing
1847 ;; operations
b05fde66
GM
1848 (mapc (lambda (d)
1849 (set-marker (aref d 0) nil)
1850 (set-marker (aref d 1) nil)
1851 (set-marker (aref d 2) nil)
1852 (set-marker (aref d 3) nil)
1853 (set-marker (aref d 4) nil)
1854 (set-marker (aref d 5) nil))
3b4a6e27
JB
1855 emerge-difference-list)
1856 ;; allow them to be garbage collected
1857 (setq emerge-difference-list nil)
1858 ;; restore the local map
1859 (use-local-map emerge-old-keymap)
1860 ;; turn off all the emerge modes
1861 (setq emerge-mode nil)
1862 (setq emerge-fast-mode nil)
1863 (setq emerge-edit-mode nil)
1864 (setq emerge-auto-advance nil)
1865 (setq emerge-skip-prefers nil)
1866 ;; restore mode line
1867 (kill-local-variable 'mode-line-buffer-identification)
1868 (let ((emerge-prefix-argument arg))
737e3892 1869 (run-hooks 'emerge-quit-hook)))
3b4a6e27
JB
1870
1871(defun emerge-select-A (&optional force)
71296446 1872 "Select the A variant of this difference.
737e3892
RS
1873Refuses to function if this difference has been edited, i.e., if it
1874is neither the A nor the B variant.
1875A prefix argument forces the variant to be selected
1876even if the difference has been edited."
3b4a6e27
JB
1877 (interactive "P")
1878 (let ((operate
b05fde66
GM
1879 (lambda ()
1880 (emerge-select-A-edit merge-begin merge-end A-begin A-end)
1881 (if emerge-auto-advance
1882 (emerge-next-difference))))
3b4a6e27 1883 (operate-no-change
b05fde66
GM
1884 (lambda () (if emerge-auto-advance
1885 (emerge-next-difference)))))
3b4a6e27
JB
1886 (emerge-select-version force operate-no-change operate operate)))
1887
1888;; Actually select the A variant
1889(defun emerge-select-A-edit (merge-begin merge-end A-begin A-end)
b05fde66 1890 (with-current-buffer
3b4a6e27
JB
1891 emerge-merge-buffer
1892 (delete-region merge-begin merge-end)
1893 (goto-char merge-begin)
1894 (insert-buffer-substring emerge-A-buffer A-begin A-end)
1895 (goto-char merge-begin)
1896 (aset diff-vector 6 'A)
1897 (emerge-refresh-mode-line)))
1898
1899(defun emerge-select-B (&optional force)
737e3892
RS
1900 "Select the B variant of this difference.
1901Refuses to function if this difference has been edited, i.e., if it
1902is neither the A nor the B variant.
1903A prefix argument forces the variant to be selected
1904even if the difference has been edited."
3b4a6e27
JB
1905 (interactive "P")
1906 (let ((operate
b05fde66
GM
1907 (lambda ()
1908 (emerge-select-B-edit merge-begin merge-end B-begin B-end)
1909 (if emerge-auto-advance
1910 (emerge-next-difference))))
3b4a6e27 1911 (operate-no-change
b05fde66
GM
1912 (lambda () (if emerge-auto-advance
1913 (emerge-next-difference)))))
3b4a6e27
JB
1914 (emerge-select-version force operate operate-no-change operate)))
1915
1916;; Actually select the B variant
1917(defun emerge-select-B-edit (merge-begin merge-end B-begin B-end)
b05fde66 1918 (with-current-buffer
3b4a6e27
JB
1919 emerge-merge-buffer
1920 (delete-region merge-begin merge-end)
1921 (goto-char merge-begin)
1922 (insert-buffer-substring emerge-B-buffer B-begin B-end)
1923 (goto-char merge-begin)
1924 (aset diff-vector 6 'B)
1925 (emerge-refresh-mode-line)))
1926
1927(defun emerge-default-A ()
737e3892
RS
1928 "Make the A variant the default from here down.
1929This selects the A variant for all differences from here down in the buffer
3b4a6e27
JB
1930which are still defaulted, i.e., which the user has not selected and for
1931which there is no preference."
1932 (interactive)
1933 (let ((buffer-read-only nil))
1934 (let ((selected-difference emerge-current-difference)
1935 (n (max emerge-current-difference 0)))
1936 (while (< n emerge-number-of-differences)
1937 (let ((diff-vector (aref emerge-difference-list n)))
1938 (if (eq (aref diff-vector 6) 'default-B)
1939 (progn
1940 (emerge-unselect-and-select-difference n t)
1941 (emerge-select-A)
1942 (aset diff-vector 6 'default-A))))
1943 (setq n (1+ n))
e00fcc05 1944 (if (zerop (% n 10))
3b4a6e27
JB
1945 (message "Setting default to A...%d" n)))
1946 (emerge-unselect-and-select-difference selected-difference)))
ff5f6ddd 1947 (message "Default choice is now A"))
3b4a6e27
JB
1948
1949(defun emerge-default-B ()
737e3892
RS
1950 "Make the B variant the default from here down.
1951This selects the B variant for all differences from here down in the buffer
3b4a6e27
JB
1952which are still defaulted, i.e., which the user has not selected and for
1953which there is no preference."
1954 (interactive)
1955 (let ((buffer-read-only nil))
1956 (let ((selected-difference emerge-current-difference)
1957 (n (max emerge-current-difference 0)))
1958 (while (< n emerge-number-of-differences)
1959 (let ((diff-vector (aref emerge-difference-list n)))
1960 (if (eq (aref diff-vector 6) 'default-A)
1961 (progn
1962 (emerge-unselect-and-select-difference n t)
1963 (emerge-select-B)
1964 (aset diff-vector 6 'default-B))))
1965 (setq n (1+ n))
e00fcc05 1966 (if (zerop (% n 10))
3b4a6e27
JB
1967 (message "Setting default to B...%d" n)))
1968 (emerge-unselect-and-select-difference selected-difference)))
ff5f6ddd 1969 (message "Default choice is now B"))
3b4a6e27
JB
1970
1971(defun emerge-fast-mode ()
737e3892
RS
1972 "Set fast mode, for Emerge.
1973In this mode ordinary Emacs commands are disabled, and Emerge commands
1974need not be prefixed with \\<emerge-fast-keymap>\\[emerge-basic-keymap]."
3b4a6e27
JB
1975 (interactive)
1976 (setq buffer-read-only t)
1977 (use-local-map emerge-fast-keymap)
1978 (setq emerge-mode t)
1979 (setq emerge-fast-mode t)
1980 (setq emerge-edit-mode nil)
1981 (message "Fast mode set")
ec165e56 1982 (force-mode-line-update))
3b4a6e27
JB
1983
1984(defun emerge-edit-mode ()
737e3892
RS
1985 "Set edit mode, for Emerge.
1986In this mode ordinary Emacs commands are available, and Emerge commands
1987must be prefixed with \\<emerge-fast-keymap>\\[emerge-basic-keymap]."
3b4a6e27
JB
1988 (interactive)
1989 (setq buffer-read-only nil)
1990 (use-local-map emerge-edit-keymap)
1991 (setq emerge-mode t)
1992 (setq emerge-fast-mode nil)
1993 (setq emerge-edit-mode t)
1994 (message "Edit mode set")
ec165e56 1995 (force-mode-line-update))
3b4a6e27
JB
1996
1997(defun emerge-auto-advance (arg)
737e3892
RS
1998 "Toggle Auto-Advance mode, for Emerge.
1999This mode causes `emerge-select-A' and `emerge-select-B' to automatically
2000advance to the next difference.
2001With a positive argument, turn on Auto-Advance mode.
2002With a negative argument, turn off Auto-Advance mode."
3b4a6e27
JB
2003 (interactive "P")
2004 (setq emerge-auto-advance (if (null arg)
2005 (not emerge-auto-advance)
2006 (> (prefix-numeric-value arg) 0)))
c880b9d5 2007 (message (if emerge-auto-advance
3b4a6e27
JB
2008 "Auto-advance set"
2009 "Auto-advance cleared"))
ec165e56 2010 (force-mode-line-update))
3b4a6e27
JB
2011
2012(defun emerge-skip-prefers (arg)
737e3892
RS
2013 "Toggle Skip-Prefers mode, for Emerge.
2014This mode causes `emerge-next-difference' and `emerge-previous-difference'
2015to automatically skip over differences for which there is a preference.
2016With a positive argument, turn on Skip-Prefers mode.
2017With a negative argument, turn off Skip-Prefers mode."
3b4a6e27
JB
2018 (interactive "P")
2019 (setq emerge-skip-prefers (if (null arg)
2020 (not emerge-skip-prefers)
2021 (> (prefix-numeric-value arg) 0)))
2022 (message (if emerge-skip-prefers
2023 "Skip-prefers set"
2024 "Skip-prefers cleared"))
ec165e56 2025 (force-mode-line-update))
3b4a6e27
JB
2026
2027(defun emerge-copy-as-kill-A ()
2028 "Put the A variant of this difference in the kill ring."
2029 (interactive)
2030 (emerge-validate-difference)
2031 (let* ((diff-vector
2032 (aref emerge-difference-list emerge-current-difference))
2033 (A-begin (1+ (aref diff-vector 0)))
2034 (A-end (1- (aref diff-vector 1)))
2035 ;; so further kills don't append
2036 this-command)
7fdbcd83 2037 (with-current-buffer emerge-A-buffer
3b4a6e27
JB
2038 (copy-region-as-kill A-begin A-end))))
2039
2040(defun emerge-copy-as-kill-B ()
2041 "Put the B variant of this difference in the kill ring."
2042 (interactive)
2043 (emerge-validate-difference)
2044 (let* ((diff-vector
2045 (aref emerge-difference-list emerge-current-difference))
2046 (B-begin (1+ (aref diff-vector 2)))
2047 (B-end (1- (aref diff-vector 3)))
2048 ;; so further kills don't append
2049 this-command)
7fdbcd83 2050 (with-current-buffer emerge-B-buffer
3b4a6e27
JB
2051 (copy-region-as-kill B-begin B-end))))
2052
2053(defun emerge-insert-A (arg)
2054 "Insert the A variant of this difference at the point.
2055Leaves point after text, mark before.
2056With prefix argument, puts point before, mark after."
2057 (interactive "P")
2058 (emerge-validate-difference)
2059 (let* ((diff-vector
2060 (aref emerge-difference-list emerge-current-difference))
2061 (A-begin (1+ (aref diff-vector 0)))
2062 (A-end (1- (aref diff-vector 1)))
2063 (opoint (point))
2064 (buffer-read-only nil))
2065 (insert-buffer-substring emerge-A-buffer A-begin A-end)
2066 (if (not arg)
2067 (set-mark opoint)
2068 (set-mark (point))
2069 (goto-char opoint))))
2070
2071(defun emerge-insert-B (arg)
2072 "Insert the B variant of this difference at the point.
2073Leaves point after text, mark before.
2074With prefix argument, puts point before, mark after."
2075 (interactive "P")
2076 (emerge-validate-difference)
2077 (let* ((diff-vector
2078 (aref emerge-difference-list emerge-current-difference))
2079 (B-begin (1+ (aref diff-vector 2)))
2080 (B-end (1- (aref diff-vector 3)))
2081 (opoint (point))
2082 (buffer-read-only nil))
2083 (insert-buffer-substring emerge-B-buffer B-begin B-end)
2084 (if (not arg)
2085 (set-mark opoint)
2086 (set-mark (point))
2087 (goto-char opoint))))
2088
2089(defun emerge-mark-difference (arg)
2090 "Leaves the point before this difference and the mark after it.
2091With prefix argument, puts mark before, point after."
2092 (interactive "P")
2093 (emerge-validate-difference)
2094 (let* ((diff-vector
2095 (aref emerge-difference-list emerge-current-difference))
2096 (merge-begin (1+ (aref diff-vector 4)))
2097 (merge-end (1- (aref diff-vector 5))))
2098 (if (not arg)
2099 (progn
2100 (goto-char merge-begin)
2101 (set-mark merge-end))
2102 (goto-char merge-end)
2103 (set-mark merge-begin))))
2104
2105(defun emerge-file-names ()
2106 "Show the names of the buffers or files being operated on by Emerge.
737e3892 2107Use C-u l to reset the windows afterward."
3b4a6e27
JB
2108 (interactive)
2109 (delete-other-windows)
737e3892 2110 (let ((temp-buffer-show-function
b05fde66 2111 (lambda (buf)
2d197ffb 2112 (split-window-below)
b05fde66
GM
2113 (switch-to-buffer buf)
2114 (other-window 1))))
3b4a6e27 2115 (with-output-to-temp-buffer "*Help*"
b05fde66 2116 (with-current-buffer emerge-A-buffer
3b4a6e27
JB
2117 (if buffer-file-name
2118 (progn
2119 (princ "File A is: ")
2120 (princ buffer-file-name))
2121 (progn
2122 (princ "Buffer A is: ")
2123 (princ (buffer-name))))
2124 (princ "\n"))
b05fde66 2125 (with-current-buffer emerge-B-buffer
3b4a6e27
JB
2126 (if buffer-file-name
2127 (progn
2128 (princ "File B is: ")
2129 (princ buffer-file-name))
2130 (progn
2131 (princ "Buffer B is: ")
2132 (princ (buffer-name))))
2133 (princ "\n"))
2134 (if emerge-ancestor-buffer
b05fde66 2135 (with-current-buffer emerge-ancestor-buffer
3b4a6e27
JB
2136 (if buffer-file-name
2137 (progn
2138 (princ "Ancestor file is: ")
2139 (princ buffer-file-name))
2140 (progn
2141 (princ "Ancestor buffer is: ")
2142 (princ (buffer-name))))
2143 (princ "\n")))
62f72ce0 2144 (princ emerge-output-description)
7fdbcd83 2145 (with-current-buffer standard-output
62f72ce0 2146 (help-mode)))))
3b4a6e27
JB
2147
2148(defun emerge-join-differences (arg)
737e3892 2149 "Join the selected difference with the following one.
eb8c3be9 2150With a prefix argument, join with the preceding one."
3b4a6e27
JB
2151 (interactive "P")
2152 (let ((n emerge-current-difference))
2153 ;; adjust n to be first difference to join
2154 (if arg
2155 (setq n (1- n)))
2156 ;; n and n+1 are the differences to join
2157 ;; check that they are both differences
2158 (if (or (< n 0) (>= n (1- emerge-number-of-differences)))
2159 (error "Incorrect differences to join"))
2160 ;; remove the flags
2161 (emerge-unselect-difference emerge-current-difference)
2162 ;; decrement total number of differences
2163 (setq emerge-number-of-differences (1- emerge-number-of-differences))
2164 ;; build new differences vector
2165 (let ((i 0)
2166 (new-differences (make-vector emerge-number-of-differences nil)))
2167 (while (< i emerge-number-of-differences)
2168 (aset new-differences i
2169 (cond
2170 ((< i n) (aref emerge-difference-list i))
2171 ((> i n) (aref emerge-difference-list (1+ i)))
2172 (t (let ((prev (aref emerge-difference-list i))
2173 (next (aref emerge-difference-list (1+ i))))
2174 (vector (aref prev 0)
2175 (aref next 1)
2176 (aref prev 2)
2177 (aref next 3)
2178 (aref prev 4)
2179 (aref next 5)
2180 (let ((ps (aref prev 6))
2181 (ns (aref next 6)))
2182 (cond
2183 ((eq ps ns)
2184 ps)
2185 ((and (or (eq ps 'B) (eq ps 'prefer-B))
2186 (or (eq ns 'B) (eq ns 'prefer-B)))
2187 'B)
2188 (t 'A))))))))
2189 (setq i (1+ i)))
2190 (setq emerge-difference-list new-differences))
2191 ;; set the current difference correctly
2192 (setq emerge-current-difference n)
2193 ;; fix the mode line
2194 (emerge-refresh-mode-line)
2195 ;; reinsert the flags
2196 (emerge-select-difference emerge-current-difference)
2197 (emerge-recenter)))
2198
2199(defun emerge-split-difference ()
2200 "Split the current difference where the points are in the three windows."
2201 (interactive)
2202 (let ((n emerge-current-difference))
2203 ;; check that this is a valid difference
2204 (emerge-validate-difference)
2205 ;; get the point values and old difference
b05fde66 2206 (let ((A-point (with-current-buffer emerge-A-buffer
3b4a6e27 2207 (point-marker)))
b05fde66 2208 (B-point (with-current-buffer emerge-B-buffer
3b4a6e27
JB
2209 (point-marker)))
2210 (merge-point (point-marker))
2211 (old-diff (aref emerge-difference-list n)))
2212 ;; check location of the points, give error if they aren't in the
2213 ;; differences
2214 (if (or (< A-point (aref old-diff 0))
2215 (> A-point (aref old-diff 1)))
2216 (error "Point outside of difference in A buffer"))
2217 (if (or (< B-point (aref old-diff 2))
2218 (> B-point (aref old-diff 3)))
2219 (error "Point outside of difference in B buffer"))
2220 (if (or (< merge-point (aref old-diff 4))
2221 (> merge-point (aref old-diff 5)))
2222 (error "Point outside of difference in merge buffer"))
2223 ;; remove the flags
2224 (emerge-unselect-difference emerge-current-difference)
2225 ;; increment total number of differences
2226 (setq emerge-number-of-differences (1+ emerge-number-of-differences))
2227 ;; build new differences vector
2228 (let ((i 0)
2229 (new-differences (make-vector emerge-number-of-differences nil)))
2230 (while (< i emerge-number-of-differences)
2231 (aset new-differences i
2232 (cond
2233 ((< i n)
2234 (aref emerge-difference-list i))
2235 ((> i (1+ n))
2236 (aref emerge-difference-list (1- i)))
2237 ((= i n)
2238 (vector (aref old-diff 0)
2239 A-point
2240 (aref old-diff 2)
2241 B-point
2242 (aref old-diff 4)
2243 merge-point
2244 (aref old-diff 6)))
2245 (t
2246 (vector (copy-marker A-point)
2247 (aref old-diff 1)
2248 (copy-marker B-point)
2249 (aref old-diff 3)
2250 (copy-marker merge-point)
2251 (aref old-diff 5)
2252 (aref old-diff 6)))))
2253 (setq i (1+ i)))
2254 (setq emerge-difference-list new-differences))
2255 ;; set the current difference correctly
2256 (setq emerge-current-difference n)
2257 ;; fix the mode line
2258 (emerge-refresh-mode-line)
2259 ;; reinsert the flags
2260 (emerge-select-difference emerge-current-difference)
2261 (emerge-recenter))))
2262
2263(defun emerge-trim-difference ()
737e3892
RS
2264 "Trim lines off top and bottom of difference that are the same.
2265If lines are the same in both the A and the B versions, strip them off.
2266\(This can happen when the A and B versions have common lines that the
2267ancestor version does not share.)"
3b4a6e27
JB
2268 (interactive)
2269 ;; make sure we are in a real difference
2270 (emerge-validate-difference)
2271 ;; remove the flags
2272 (emerge-unselect-difference emerge-current-difference)
2273 (let* ((diff (aref emerge-difference-list emerge-current-difference))
2274 (top-a (marker-position (aref diff 0)))
2275 (bottom-a (marker-position (aref diff 1)))
2276 (top-b (marker-position (aref diff 2)))
2277 (bottom-b (marker-position (aref diff 3)))
2278 (top-m (marker-position (aref diff 4)))
2279 (bottom-m (marker-position (aref diff 5)))
2280 size success sa sb sm)
2281 ;; move down the tops of the difference regions as much as possible
2282 ;; Try advancing comparing 1000 chars at a time.
2283 ;; When that fails, go 500 chars at a time, and so on.
2284 (setq size 1000)
2285 (while (> size 0)
2286 (setq success t)
2287 (while success
2288 (setq size (min size (- bottom-a top-a) (- bottom-b top-b)
2289 (- bottom-m top-m)))
b05fde66 2290 (setq sa (with-current-buffer emerge-A-buffer
3b4a6e27
JB
2291 (buffer-substring top-a
2292 (+ size top-a))))
b05fde66 2293 (setq sb (with-current-buffer emerge-B-buffer
3b4a6e27
JB
2294 (buffer-substring top-b
2295 (+ size top-b))))
2296 (setq sm (buffer-substring top-m (+ size top-m)))
2297 (setq success (and (> size 0) (equal sa sb) (equal sb sm)))
2298 (if success
2299 (setq top-a (+ top-a size)
2300 top-b (+ top-b size)
2301 top-m (+ top-m size))))
2302 (setq size (/ size 2)))
2303 ;; move up the bottoms of the difference regions as much as possible
2304 ;; Try advancing comparing 1000 chars at a time.
2305 ;; When that fails, go 500 chars at a time, and so on.
2306 (setq size 1000)
2307 (while (> size 0)
2308 (setq success t)
2309 (while success
2310 (setq size (min size (- bottom-a top-a) (- bottom-b top-b)
2311 (- bottom-m top-m)))
b05fde66 2312 (setq sa (with-current-buffer emerge-A-buffer
3b4a6e27
JB
2313 (buffer-substring (- bottom-a size)
2314 bottom-a)))
b05fde66 2315 (setq sb (with-current-buffer emerge-B-buffer
3b4a6e27
JB
2316 (buffer-substring (- bottom-b size)
2317 bottom-b)))
2318 (setq sm (buffer-substring (- bottom-m size) bottom-m))
2319 (setq success (and (> size 0) (equal sa sb) (equal sb sm)))
2320 (if success
2321 (setq bottom-a (- bottom-a size)
2322 bottom-b (- bottom-b size)
2323 bottom-m (- bottom-m size))))
2324 (setq size (/ size 2)))
2325 ;; {top,bottom}-{a,b,m} are now set at the new beginnings and ends
2326 ;; of the difference regions. Move them to the beginning of lines, as
2327 ;; appropriate.
b05fde66 2328 (with-current-buffer emerge-A-buffer
3b4a6e27
JB
2329 (goto-char top-a)
2330 (beginning-of-line)
2331 (aset diff 0 (point-marker))
2332 (goto-char bottom-a)
2333 (beginning-of-line 2)
2334 (aset diff 1 (point-marker)))
b05fde66 2335 (with-current-buffer emerge-B-buffer
3b4a6e27
JB
2336 (goto-char top-b)
2337 (beginning-of-line)
2338 (aset diff 2 (point-marker))
2339 (goto-char bottom-b)
2340 (beginning-of-line 2)
2341 (aset diff 3 (point-marker)))
2342 (goto-char top-m)
2343 (beginning-of-line)
2344 (aset diff 4 (point-marker))
2345 (goto-char bottom-m)
2346 (beginning-of-line 2)
2347 (aset diff 5 (point-marker))
2348 ;; put the flags back in, recenter the display
2349 (emerge-select-difference emerge-current-difference)
2350 (emerge-recenter)))
2351
623a8830
GM
2352;; FIXME the manual advertised this as working in the A or B buffers,
2353;; but it does not, because all the buffer locals are nil there.
2354;; It would work to call it from the merge buffer and specify that one
2355;; wants to use the value of point in the A or B buffer.
2356;; But with the prefix argument already in use, there is no easy way
2357;; to have it ask for a buffer.
3b4a6e27
JB
2358(defun emerge-find-difference (arg)
2359 "Find the difference containing the current position of the point.
2360If there is no containing difference and the prefix argument is positive,
2361it finds the nearest following difference. A negative prefix argument finds
661d3230 2362the nearest previous difference."
8bfce8a7 2363 (interactive "P")
661d3230
RS
2364 (cond ((eq (current-buffer) emerge-A-buffer)
2365 (emerge-find-difference-A arg))
2366 ((eq (current-buffer) emerge-B-buffer)
2367 (emerge-find-difference-B arg))
2368 (t (emerge-find-difference-merge arg))))
2369
2370(defun emerge-find-difference-merge (arg)
2371 "Find the difference containing point, in the merge buffer.
2372If there is no containing difference and the prefix argument is positive,
2373it finds the nearest following difference. A negative prefix argument finds
3b4a6e27
JB
2374the nearest previous difference."
2375 (interactive "P")
2376 ;; search for the point in the merge buffer, using the markers
2377 ;; for the beginning and end of the differences in the merge buffer
2378 (emerge-find-difference1 arg (point) 4 5))
2379
2380(defun emerge-find-difference-A (arg)
661d3230 2381 "Find the difference containing point, in the A buffer.
737e3892 2382This command must be executed in the merge buffer.
3b4a6e27
JB
2383If there is no containing difference and the prefix argument is positive,
2384it finds the nearest following difference. A negative prefix argument finds
2385the nearest previous difference."
2386 (interactive "P")
2387 ;; search for the point in the A buffer, using the markers
2388 ;; for the beginning and end of the differences in the A buffer
2389 (emerge-find-difference1 arg
b05fde66 2390 (with-current-buffer emerge-A-buffer (point))
3b4a6e27
JB
2391 0 1))
2392
2393(defun emerge-find-difference-B (arg)
661d3230 2394 "Find the difference containing point, in the B buffer.
737e3892 2395This command must be executed in the merge buffer.
3b4a6e27
JB
2396If there is no containing difference and the prefix argument is positive,
2397it finds the nearest following difference. A negative prefix argument finds
2398the nearest previous difference."
2399 (interactive "P")
2400 ;; search for the point in the B buffer, using the markers
2401 ;; for the beginning and end of the differences in the B buffer
2402 (emerge-find-difference1 arg
b05fde66 2403 (with-current-buffer emerge-B-buffer (point))
3b4a6e27
JB
2404 2 3))
2405
2406(defun emerge-find-difference1 (arg location begin end)
2407 (let* ((index
2408 ;; find first difference containing or after the current position
2409 (catch 'search
2410 (let ((n 0))
2411 (while (< n emerge-number-of-differences)
2412 (let ((diff-vector (aref emerge-difference-list n)))
2413 (if (<= location (marker-position (aref diff-vector end)))
2414 (throw 'search n)))
2415 (setq n (1+ n))))
2416 emerge-number-of-differences))
2417 (contains
2418 ;; whether the found difference contains the current position
2419 (and (< index emerge-number-of-differences)
2420 (<= (marker-position (aref (aref emerge-difference-list index)
2421 begin))
2422 location)))
2423 (arg-value
2424 ;; numeric value of prefix argument
2425 (prefix-numeric-value arg)))
2426 (emerge-unselect-and-select-difference
2427 (cond
2428 ;; if the point is in a difference, select it
2429 (contains index)
2430 ;; if the arg is nil and the point is not in a difference, error
2431 ((null arg) (error "No difference contains point"))
2432 ;; if the arg is positive, select the following difference
2433 ((> arg-value 0)
2434 (if (< index emerge-number-of-differences)
2435 index
2436 (error "No difference contains or follows point")))
eb8c3be9 2437 ;; if the arg is negative, select the preceding difference
3b4a6e27
JB
2438 (t
2439 (if (> index 0)
2440 (1- index)
eb8c3be9 2441 (error "No difference contains or precedes point")))))))
3b4a6e27
JB
2442
2443(defun emerge-line-numbers ()
737e3892
RS
2444 "Display the current line numbers.
2445This function displays the line numbers of the points in the A, B, and
3b4a6e27
JB
2446merge buffers."
2447 (interactive)
2448 (let* ((valid-diff
2449 (and (>= emerge-current-difference 0)
2450 (< emerge-current-difference emerge-number-of-differences)))
b05fde66
GM
2451 (emerge-line-diff (and valid-diff
2452 (aref emerge-difference-list
2453 emerge-current-difference)))
2454 (merge-line (emerge-line-number-in-buf 4 5))
2455 (A-line (with-current-buffer emerge-A-buffer
2456 (emerge-line-number-in-buf 0 1)))
2457 (B-line (with-current-buffer emerge-B-buffer
2458 (emerge-line-number-in-buf 2 3))))
3b4a6e27
JB
2459 (message "At lines: merge = %d, A = %d, B = %d"
2460 merge-line A-line B-line)))
2461
b05fde66
GM
2462(defvar emerge-line-diff)
2463
3b4a6e27 2464(defun emerge-line-number-in-buf (begin-marker end-marker)
9b026d9f
GM
2465 ;; FIXME point-min rather than 1? widen?
2466 (let ((temp (1+ (count-lines 1 (line-beginning-position)))))
3b4a6e27
JB
2467 (if valid-diff
2468 (progn
b05fde66 2469 (if (> (point) (aref emerge-line-diff begin-marker))
3b4a6e27 2470 (setq temp (- temp emerge-before-flag-lines)))
b05fde66 2471 (if (> (point) (aref emerge-line-diff end-marker))
3b4a6e27
JB
2472 (setq temp (- temp emerge-after-flag-lines)))))
2473 temp))
2474
ff5f6ddd
RS
2475(defun emerge-set-combine-template (string &optional localize)
2476 "Set `emerge-combine-versions-template' to STRING.
2477This value controls how `emerge-combine-versions' combines the two versions.
2478With prefix argument, `emerge-combine-versions-template' is made local to this
2479merge buffer. Localization is permanent for any particular merge buffer."
2480 (interactive "s\nP")
2481 (if localize
2482 (make-local-variable 'emerge-combine-versions-template))
2483 (setq emerge-combine-versions-template string)
2484 (message
2485 (if (assq 'emerge-combine-versions-template (buffer-local-variables))
2486 "emerge-set-combine-versions-template set locally"
2487 "emerge-set-combine-versions-template set")))
2488
3b4a6e27 2489(defun emerge-set-combine-versions-template (start end &optional localize)
737e3892
RS
2490 "Copy region into `emerge-combine-versions-template'.
2491This controls how `emerge-combine-versions' will combine the two versions.
ff5f6ddd 2492With prefix argument, `emerge-combine-versions-template' is made local to this
3b4a6e27
JB
2493merge buffer. Localization is permanent for any particular merge buffer."
2494 (interactive "r\nP")
2495 (if localize
2496 (make-local-variable 'emerge-combine-versions-template))
2497 (setq emerge-combine-versions-template (buffer-substring start end))
2498 (message
2499 (if (assq 'emerge-combine-versions-template (buffer-local-variables))
2500 "emerge-set-combine-versions-template set locally."
2501 "emerge-set-combine-versions-template set.")))
2502
2503(defun emerge-combine-versions (&optional force)
737e3892 2504 "Combine versions using the template in `emerge-combine-versions-template'.
3b4a6e27
JB
2505Refuses to function if this difference has been edited, i.e., if it is
2506neither the A nor the B variant.
737e3892 2507An argument forces the variant to be selected even if the difference has
3b4a6e27
JB
2508been edited."
2509 (interactive "P")
2510 (emerge-combine-versions-internal emerge-combine-versions-template force))
2511
2512(defun emerge-combine-versions-register (char &optional force)
2513 "Combine the two versions using the template in register REG.
737e3892 2514See documentation of the variable `emerge-combine-versions-template'
3b4a6e27
JB
2515for how the template is interpreted.
2516Refuses to function if this difference has been edited, i.e., if it is
2517neither the A nor the B variant.
737e3892 2518An argument forces the variant to be selected even if the difference has
3b4a6e27
JB
2519been edited."
2520 (interactive "cRegister containing template: \nP")
2521 (let ((template (get-register char)))
2522 (if (not (stringp template))
2523 (error "Register does not contain text"))
2524 (emerge-combine-versions-internal template force)))
2525
b05fde66 2526(defun emerge-combine-versions-internal (emerge-combine-template force)
3b4a6e27 2527 (let ((operate
b05fde66
GM
2528 (lambda ()
2529 (emerge-combine-versions-edit merge-begin merge-end
2530 A-begin A-end B-begin B-end)
2531 (if emerge-auto-advance
2532 (emerge-next-difference)))))
3b4a6e27
JB
2533 (emerge-select-version force operate operate operate)))
2534
b05fde66
GM
2535(defvar emerge-combine-template)
2536
3b4a6e27
JB
2537(defun emerge-combine-versions-edit (merge-begin merge-end
2538 A-begin A-end B-begin B-end)
b05fde66 2539 (with-current-buffer
3b4a6e27
JB
2540 emerge-merge-buffer
2541 (delete-region merge-begin merge-end)
2542 (goto-char merge-begin)
2543 (let ((i 0))
b05fde66
GM
2544 (while (< i (length emerge-combine-template))
2545 (let ((c (aref emerge-combine-template i)))
3b4a6e27
JB
2546 (if (= c ?%)
2547 (progn
2548 (setq i (1+ i))
71296446 2549 (setq c
3b4a6e27 2550 (condition-case nil
b05fde66 2551 (aref emerge-combine-template i)
3b4a6e27
JB
2552 (error ?%)))
2553 (cond ((= c ?a)
2554 (insert-buffer-substring emerge-A-buffer A-begin A-end))
71296446 2555 ((= c ?b)
3b4a6e27 2556 (insert-buffer-substring emerge-B-buffer B-begin B-end))
71296446 2557 ((= c ?%)
737e3892
RS
2558 (insert ?%))
2559 (t
2560 (insert c))))
3b4a6e27
JB
2561 (insert c)))
2562 (setq i (1+ i))))
2563 (goto-char merge-begin)
2564 (aset diff-vector 6 'combined)
2565 (emerge-refresh-mode-line)))
2566
2567(defun emerge-set-merge-mode (mode)
737e3892
RS
2568 "Set the major mode in a merge buffer.
2569Overrides any change that the mode might make to the mode line or local
2570keymap. Leaves merge in fast mode."
3b4a6e27
JB
2571 (interactive
2572 (list (intern (completing-read "New major mode for merge buffer: "
2573 obarray 'commandp t nil))))
2574 (funcall mode)
2575 (emerge-refresh-mode-line)
2576 (if emerge-fast-mode
2577 (emerge-fast-mode)
2578 (emerge-edit-mode)))
2579
2580(defun emerge-one-line-window ()
2581 (interactive)
2582 (let ((window-min-height 1))
2583 (shrink-window (- (window-height) 2))))
2584
2585;;; Support routines
2586
2587;; Select a difference by placing the visual flags around the appropriate
2588;; group of lines in the A, B, and merge buffers
2589(defun emerge-select-difference (n)
151e4b9c
RS
2590 (let ((emerge-globalized-difference-list emerge-difference-list)
2591 (emerge-globalized-number-of-differences emerge-number-of-differences))
2592 (emerge-place-flags-in-buffer emerge-A-buffer n 0 1)
2593 (emerge-place-flags-in-buffer emerge-B-buffer n 2 3)
2594 (emerge-place-flags-in-buffer nil n 4 5))
737e3892 2595 (run-hooks 'emerge-select-hook))
151e4b9c
RS
2596
2597(defun emerge-place-flags-in-buffer (buffer difference before-index
2598 after-index)
2599 (if buffer
b05fde66 2600 (with-current-buffer
151e4b9c
RS
2601 buffer
2602 (emerge-place-flags-in-buffer1 difference before-index after-index))
2603 (emerge-place-flags-in-buffer1 difference before-index after-index)))
2604
2605(defun emerge-place-flags-in-buffer1 (difference before-index after-index)
3b4a6e27 2606 (let ((buffer-read-only nil))
151e4b9c
RS
2607 ;; insert the flag before the difference
2608 (let ((before (aref (aref emerge-globalized-difference-list difference)
2609 before-index))
2610 here)
2611 (goto-char before)
2612 ;; insert the flag itself
2613 (insert-before-markers emerge-before-flag)
2614 (setq here (point))
2615 ;; Put the marker(s) referring to this position 1 character before the
2616 ;; end of the flag, so it won't be damaged by the user.
2617 ;; This gets a bit tricky, as there could be a number of markers
2618 ;; that have to be moved.
2619 (set-marker before (1- before))
2620 (let ((n (1- difference)) after-marker before-marker diff-list)
2621 (while (and
2622 (>= n 0)
2623 (progn
2624 (setq diff-list (aref emerge-globalized-difference-list n)
2625 after-marker (aref diff-list after-index))
2626 (= after-marker here)))
2627 (set-marker after-marker (1- after-marker))
2628 (setq before-marker (aref diff-list before-index))
2629 (if (= before-marker here)
2630 (setq before-marker (1- before-marker)))
2631 (setq n (1- n)))))
2632 ;; insert the flag after the difference
2633 (let* ((after (aref (aref emerge-globalized-difference-list difference)
2634 after-index))
2635 (here (marker-position after)))
2636 (goto-char here)
2637 ;; insert the flag itself
2638 (insert emerge-after-flag)
2639 ;; Put the marker(s) referring to this position 1 character after the
2640 ;; beginning of the flag, so it won't be damaged by the user.
2641 ;; This gets a bit tricky, as there could be a number of markers
2642 ;; that have to be moved.
2643 (set-marker after (1+ after))
2644 (let ((n (1+ difference)) before-marker after-marker diff-list)
2645 (while (and
2646 (< n emerge-globalized-number-of-differences)
2647 (progn
2648 (setq diff-list (aref emerge-globalized-difference-list n)
2649 before-marker (aref diff-list before-index))
2650 (= before-marker here)))
2651 (set-marker before-marker (1+ before-marker))
2652 (setq after-marker (aref diff-list after-index))
2653 (if (= after-marker here)
2654 (setq after-marker (1+ after-marker)))
2655 (setq n (1+ n)))))))
3b4a6e27
JB
2656
2657;; Unselect a difference by removing the visual flags in the buffers.
2658(defun emerge-unselect-difference (n)
2659 (let ((diff-vector (aref emerge-difference-list n)))
2660 (emerge-remove-flags-in-buffer emerge-A-buffer
2661 (aref diff-vector 0) (aref diff-vector 1))
2662 (emerge-remove-flags-in-buffer emerge-B-buffer
2663 (aref diff-vector 2) (aref diff-vector 3))
2664 (emerge-remove-flags-in-buffer emerge-merge-buffer
151e4b9c 2665 (aref diff-vector 4) (aref diff-vector 5)))
737e3892 2666 (run-hooks 'emerge-unselect-hook))
3b4a6e27
JB
2667
2668(defun emerge-remove-flags-in-buffer (buffer before after)
b05fde66 2669 (with-current-buffer
3b4a6e27
JB
2670 buffer
2671 (let ((buffer-read-only nil))
151e4b9c
RS
2672 ;; remove the flags, if they're there
2673 (goto-char (- before (1- emerge-before-flag-length)))
3b4a6e27
JB
2674 (if (looking-at emerge-before-flag-match)
2675 (delete-char emerge-before-flag-length)
2676 ;; the flag isn't there
2677 (ding)
737e3892 2678 (message "Trouble removing flag"))
151e4b9c 2679 (goto-char (1- after))
3b4a6e27
JB
2680 (if (looking-at emerge-after-flag-match)
2681 (delete-char emerge-after-flag-length)
2682 ;; the flag isn't there
2683 (ding)
737e3892 2684 (message "Trouble removing flag")))))
3b4a6e27 2685
151e4b9c 2686;; Select a difference, removing any flags that exist now.
3b4a6e27
JB
2687(defun emerge-unselect-and-select-difference (n &optional suppress-display)
2688 (if (and (>= emerge-current-difference 0)
2689 (< emerge-current-difference emerge-number-of-differences))
2690 (emerge-unselect-difference emerge-current-difference))
2691 (if (and (>= n 0) (< n emerge-number-of-differences))
2692 (progn
2693 (emerge-select-difference n)
2694 (let* ((diff-vector (aref emerge-difference-list n))
2695 (selection-type (aref diff-vector 6)))
2696 (if (eq selection-type 'default-A)
2697 (aset diff-vector 6 'A)
2698 (if (eq selection-type 'default-B)
2699 (aset diff-vector 6 'B))))))
2700 (setq emerge-current-difference n)
2701 (if (not suppress-display)
2702 (progn
2703 (emerge-recenter)
2704 (emerge-refresh-mode-line))))
2705
2706;; Perform tests to see whether user should be allowed to select a version
2707;; of this difference:
2708;; a valid difference has been selected; and
2709;; the difference text in the merge buffer is:
2710;; the A version (execute a-version), or
2711;; the B version (execute b-version), or
2712;; empty (execute neither-version), or
2713;; argument FORCE is true (execute neither-version)
2714;; Otherwise, signal an error.
2715(defun emerge-select-version (force a-version b-version neither-version)
2716 (emerge-validate-difference)
2717 (let ((buffer-read-only nil))
2718 (let* ((diff-vector
2719 (aref emerge-difference-list emerge-current-difference))
2720 (A-begin (1+ (aref diff-vector 0)))
2721 (A-end (1- (aref diff-vector 1)))
2722 (B-begin (1+ (aref diff-vector 2)))
2723 (B-end (1- (aref diff-vector 3)))
2724 (merge-begin (1+ (aref diff-vector 4)))
2725 (merge-end (1- (aref diff-vector 5))))
2726 (if (emerge-compare-buffers emerge-A-buffer A-begin A-end
2727 emerge-merge-buffer merge-begin
2728 merge-end)
2729 (funcall a-version)
2730 (if (emerge-compare-buffers emerge-B-buffer B-begin B-end
2731 emerge-merge-buffer merge-begin
2732 merge-end)
2733 (funcall b-version)
2734 (if (or force (= merge-begin merge-end))
2735 (funcall neither-version)
151e4b9c
RS
2736 (error "This difference region has been edited")))))))
2737
2738;; Read a file name, handling all of the various defaulting rules.
2739
2740(defun emerge-read-file-name (prompt alternative-default-dir default-file
90371ec9 2741 A-file must-match)
737e3892 2742 ;; `prompt' should not have trailing ": ", so that it can be modified
151e4b9c
RS
2743 ;; according to context.
2744 ;; If alternative-default-dir is non-nil, it should be used as the default
2745 ;; directory instead if default-directory, if emerge-default-last-directories
2746 ;; is set.
2747 ;; If default-file is set, it should be used as the default value.
2748 ;; If A-file is set, and its directory is different from
2749 ;; alternative-default-dir, and if emerge-default-last-directories is set,
2750 ;; the default file should be the last part of A-file in the default
2751 ;; directory. (Overriding default-file.)
2752 (cond
2753 ;; If this is not the A-file argument (shown by non-nil A-file), and
2754 ;; if emerge-default-last-directories is set, and
2755 ;; the default directory exists but is not the same as the directory of the
2756 ;; A-file,
2757 ;; then make the default file have the same name as the A-file, but in
2758 ;; the default directory.
2759 ((and emerge-default-last-directories
2760 A-file
2761 alternative-default-dir
2762 (not (string-equal alternative-default-dir
2763 (file-name-directory A-file))))
2764 (read-file-name (format "%s (default %s): "
2765 prompt (file-name-nondirectory A-file))
2766 alternative-default-dir
2767 (concat alternative-default-dir
2768 (file-name-nondirectory A-file))
90371ec9 2769 (and must-match 'confirm)))
151e4b9c
RS
2770 ;; If there is a default file, use it.
2771 (default-file
2772 (read-file-name (format "%s (default %s): " prompt default-file)
2773 ;; If emerge-default-last-directories is set, use the
2774 ;; directory from the same argument of the last call of
2775 ;; Emerge as the default for this argument.
2776 (and emerge-default-last-directories
2777 alternative-default-dir)
90371ec9 2778 default-file (and must-match 'confirm)))
151e4b9c
RS
2779 (t
2780 (read-file-name (concat prompt ": ")
2781 ;; If emerge-default-last-directories is set, use the
2782 ;; directory from the same argument of the last call of
2783 ;; Emerge as the default for this argument.
2784 (and emerge-default-last-directories
2785 alternative-default-dir)
90371ec9 2786 nil (and must-match 'confirm)))))
3b4a6e27
JB
2787
2788;; Revise the mode line to display which difference we have selected
2789
2790(defun emerge-refresh-mode-line ()
2791 (setq mode-line-buffer-identification
2792 (list (format "Emerge: %%b diff %d of %d%s"
2793 (1+ emerge-current-difference)
2794 emerge-number-of-differences
2795 (if (and (>= emerge-current-difference 0)
2796 (< emerge-current-difference
2797 emerge-number-of-differences))
2798 (cdr (assq (aref (aref emerge-difference-list
2799 emerge-current-difference)
2800 6)
2801 '((A . " - A")
2802 (B . " - B")
2803 (prefer-A . " - A*")
2804 (prefer-B . " - B*")
2805 (combined . " - comb"))))
2806 ""))))
ec165e56 2807 (force-mode-line-update))
3b4a6e27
JB
2808
2809;; compare two regions in two buffers for containing the same text
2810(defun emerge-compare-buffers (buffer-x x-begin x-end buffer-y y-begin y-end)
2811 ;; first check that the two regions are the same length
2812 (if (not (and (= (- x-end x-begin) (- y-end y-begin))))
2813 nil
2814 (catch 'exit
2815 (while (< x-begin x-end)
2816 ;; bite off and compare no more than 1000 characters at a time
2817 (let* ((compare-length (min (- x-end x-begin) 1000))
b05fde66 2818 (x-string (with-current-buffer
3b4a6e27
JB
2819 buffer-x
2820 (buffer-substring x-begin
2821 (+ x-begin compare-length))))
b05fde66 2822 (y-string (with-current-buffer
3b4a6e27
JB
2823 buffer-y
2824 (buffer-substring y-begin
2825 (+ y-begin compare-length)))))
2826 (if (not (string-equal x-string y-string))
2827 (throw 'exit nil)
2828 (setq x-begin (+ x-begin compare-length))
2829 (setq y-begin (+ y-begin compare-length)))))
2830 t)))
2831
2832;; Construct a unique buffer name.
71296446 2833;; The first one tried is prefixsuffix, then prefix<2>suffix,
3b4a6e27
JB
2834;; prefix<3>suffix, etc.
2835(defun emerge-unique-buffer-name (prefix suffix)
2836 (if (null (get-buffer (concat prefix suffix)))
2837 (concat prefix suffix)
2838 (let ((n 2))
2839 (while (get-buffer (format "%s<%d>%s" prefix n suffix))
2840 (setq n (1+ n)))
2841 (format "%s<%d>%s" prefix n suffix))))
2842
2843;; Verify that we have a difference selected.
2844(defun emerge-validate-difference ()
2845 (if (not (and (>= emerge-current-difference 0)
2846 (< emerge-current-difference emerge-number-of-differences)))
2847 (error "No difference selected")))
2848
2849;;; Functions for saving and restoring a batch of variables
2850
2851;; These functions save (get the values of) and restore (set the values of)
2852;; a list of variables. The argument is a list of symbols (the names of
2853;; the variables). A list element can also be a list of two functions,
2854;; the first of which (when called with no arguments) gets the value, and
a7acbbe4 2855;; the second (when called with a value as an argument) sets the value.
3b4a6e27
JB
2856;; A "function" is anything that funcall can handle as an argument.
2857
2858(defun emerge-save-variables (vars)
b05fde66
GM
2859 (mapcar (lambda (v) (if (symbolp v)
2860 (symbol-value v)
2861 (funcall (car v))))
3b4a6e27
JB
2862 vars))
2863
2864(defun emerge-restore-variables (vars values)
2865 (while vars
2866 (let ((var (car vars))
2867 (value (car values)))
2868 (if (symbolp var)
2869 (set var value)
2870 (funcall (car (cdr var)) value)))
2871 (setq vars (cdr vars))
2872 (setq values (cdr values))))
2873
2874;; Make a temporary file that only we have access to.
2875;; PREFIX is appended to emerge-temp-file-prefix to make the filename prefix.
2876(defun emerge-make-temp-file (prefix)
78c56e70
GM
2877 (let (f (old-modes (default-file-modes)))
2878 (unwind-protect
2879 (progn
2880 (set-default-file-modes emerge-temp-file-mode)
2881 (setq f (make-temp-file (concat emerge-temp-file-prefix prefix))))
2882 (set-default-file-modes old-modes))
3b4a6e27
JB
2883 f))
2884
2885;;; Functions that query the user before he can write out the current buffer.
2886
2887(defun emerge-query-write-file ()
737e3892
RS
2888 "Ask the user whether to write out an incomplete merge.
2889If answer is yes, call `write-file' to do so. See `emerge-query-and-call'
3b4a6e27
JB
2890for details of the querying process."
2891 (interactive)
2892 (emerge-query-and-call 'write-file))
2893
2894(defun emerge-query-save-buffer ()
737e3892
RS
2895 "Ask the user whether to save an incomplete merge.
2896If answer is yes, call `save-buffer' to do so. See `emerge-query-and-call'
3b4a6e27
JB
2897for details of the querying process."
2898 (interactive)
2899 (emerge-query-and-call 'save-buffer))
2900
2901(defun emerge-query-and-call (command)
737e3892
RS
2902 "Ask the user whether to save or write out the incomplete merge.
2903If answer is yes, call COMMAND interactively. During the call, the flags
3b4a6e27
JB
2904around the current difference are removed."
2905 (if (yes-or-no-p "Do you really write to write out this unfinished merge? ")
2906 ;; He really wants to do it -- unselect the difference for the duration
2907 (progn
2908 (if (and (>= emerge-current-difference 0)
2909 (< emerge-current-difference emerge-number-of-differences))
2910 (emerge-unselect-difference emerge-current-difference))
2911 ;; call-interactively takes the value of current-prefix-arg as the
2912 ;; prefix argument value to be passed to the command. Thus, we have
2913 ;; to do nothing special to make sure the prefix argument is
2914 ;; transmitted to the command.
2915 (call-interactively command)
2916 (if (and (>= emerge-current-difference 0)
2917 (< emerge-current-difference emerge-number-of-differences))
2918 (progn
2919 (emerge-select-difference emerge-current-difference)
2920 (emerge-recenter))))
2921 ;; He's being smart and not doing it
2922 (message "Not written")))
2923
2924;; Make sure the current buffer (for a file) has the same contents as the
2925;; file on disk, and attempt to remedy the situation if not.
2926;; Signal an error if we can't make them the same, or the user doesn't want
2927;; to do what is necessary to make them the same.
2928(defun emerge-verify-file-buffer ()
2929 ;; First check if the file has been modified since the buffer visited it.
2930 (if (verify-visited-file-modtime (current-buffer))
2931 (if (buffer-modified-p)
2932 ;; If buffer is not obsolete and is modified, offer to save
2933 (if (yes-or-no-p (format "Save file %s? " buffer-file-name))
2934 (save-buffer)
2935 (error "Buffer out of sync for file %s" buffer-file-name))
2936 ;; If buffer is not obsolete and is not modified, do nothing
2937 nil)
2938 (if (buffer-modified-p)
2939 ;; If buffer is obsolete and is modified, give error
2940 (error "Buffer out of sync for file %s" buffer-file-name)
2941 ;; If buffer is obsolete and is not modified, offer to revert
2942 (if (yes-or-no-p (format "Revert file %s? " buffer-file-name))
2943 (revert-buffer t t)
2944 (error "Buffer out of sync for file %s" buffer-file-name)))))
2945\f
2946;; Utilities that might have value outside of Emerge.
2947
2948;; Set up the mode in the current buffer to duplicate the mode in another
2949;; buffer.
2950(defun emerge-copy-modes (buffer)
2951 ;; Set the major mode
b05fde66 2952 (funcall (with-current-buffer buffer major-mode)))
3b4a6e27
JB
2953
2954;; Define a key, even if a prefix of it is defined
2955(defun emerge-force-define-key (keymap key definition)
737e3892
RS
2956 "Like `define-key', but forcibly creates prefix characters as needed.
2957If some prefix of KEY has a non-prefix definition, it is redefined."
3b4a6e27
JB
2958 ;; Find out if a prefix of key is defined
2959 (let ((v (lookup-key keymap key)))
2960 ;; If so, undefine it
2961 (if (integerp v)
2962 (define-key keymap (substring key 0 v) nil)))
2963 ;; Now define the key
2964 (define-key keymap key definition))
2965
737e3892
RS
2966;;;;; Improvements to describe-mode, so that it describes minor modes as well
2967;;;;; as the major mode
2968;;(defun describe-mode (&optional minor)
2969;; "Display documentation of current major mode.
2970;;If optional arg MINOR is non-nil (or prefix argument is given if interactive),
2971;;display documentation of active minor modes as well.
2972;;For this to work correctly for a minor mode, the mode's indicator variable
2973;;\(listed in `minor-mode-alist') must also be a function whose documentation
2974;;describes the minor mode."
2975;; (interactive)
2976;; (with-output-to-temp-buffer "*Help*"
2977;; (princ mode-name)
2978;; (princ " Mode:\n")
2979;; (princ (documentation major-mode))
2980;; (let ((minor-modes minor-mode-alist)
2981;; (locals (buffer-local-variables)))
2982;; (while minor-modes
2983;; (let* ((minor-mode (car (car minor-modes)))
2984;; (indicator (car (cdr (car minor-modes))))
2985;; (local-binding (assq minor-mode locals)))
2986;; ;; Document a minor mode if it is listed in minor-mode-alist,
2987;; ;; bound locally in this buffer, non-nil, and has a function
2988;; ;; definition.
2989;; (if (and local-binding
2990;; (cdr local-binding)
2991;; (fboundp minor-mode))
2992;; (progn
2993;; (princ (format "\n\n\n%s minor mode (indicator%s):\n"
2994;; minor-mode indicator))
2995;; (princ (documentation minor-mode)))))
2996;; (setq minor-modes (cdr minor-modes))))
7fdbcd83 2997;; (with-current-buffer standard-output
62f72ce0 2998;; (help-mode))
d5d105e8 2999;; (help-print-return-message)))
737e3892
RS
3000
3001;; This goes with the redefinition of describe-mode.
3002;;;; Adjust things so that keyboard macro definitions are documented correctly.
3003;;(fset 'defining-kbd-macro (symbol-function 'start-kbd-macro))
3004
3005;; substitute-key-definition should work now.
3006;;;; Function to shadow a definition in a keymap with definitions in another.
3007;;(defun emerge-shadow-key-definition (olddef newdef keymap shadowmap)
3008;; "Shadow OLDDEF with NEWDEF for any keys in KEYMAP with entries in SHADOWMAP.
3009;;In other words, SHADOWMAP will now shadow all definitions of OLDDEF in KEYMAP
3010;;with NEWDEF. Does not affect keys that are already defined in SHADOWMAP,
3011;;including those whose definition is OLDDEF."
3012;; ;; loop through all keymaps accessible from keymap
3013;; (let ((maps (accessible-keymaps keymap)))
3014;; (while maps
3015;; (let ((prefix (car (car maps)))
3016;; (map (cdr (car maps))))
3017;; ;; examine a keymap
3018;; (if (arrayp map)
3019;; ;; array keymap
3020;; (let ((len (length map))
3021;; (i 0))
3022;; (while (< i len)
3023;; (if (eq (aref map i) olddef)
3024;; ;; set the shadowing definition
3025;; (let ((key (concat prefix (char-to-string i))))
3026;; (emerge-define-key-if-possible shadowmap key newdef)))
3027;; (setq i (1+ i))))
3028;; ;; sparse keymap
3029;; (while map
3030;; (if (eq (cdr-safe (car-safe map)) olddef)
3031;; ;; set the shadowing definition
3032;; (let ((key
3033;; (concat prefix (char-to-string (car (car map))))))
3034;; (emerge-define-key-if-possible shadowmap key newdef)))
3035;; (setq map (cdr map)))))
3036;; (setq maps (cdr maps)))))
3b4a6e27
JB
3037
3038;; Define a key if it (or a prefix) is not already defined in the map.
3039(defun emerge-define-key-if-possible (keymap key definition)
3040 ;; look up the present definition of the key
3041 (let ((present (lookup-key keymap key)))
3042 (if (integerp present)
3043 ;; if it is "too long", look up the valid prefix
3044 (if (not (lookup-key keymap (substring key 0 present)))
3045 ;; if the prefix isn't defined, define it
3046 (define-key keymap key definition))
3047 ;; if there is no present definition, define it
3048 (if (not present)
3049 (define-key keymap key definition)))))
3050
737e3892
RS
3051;; Ordinary substitute-key-definition should do this now.
3052;;(defun emerge-recursively-substitute-key-definition (olddef newdef keymap)
3053;; "Like `substitute-key-definition', but act recursively on subkeymaps.
3054;;Make sure that subordinate keymaps aren't shared with other keymaps!
3055;;\(`copy-keymap' will suffice.)"
3056;; ;; Loop through all keymaps accessible from keymap
3057;; (let ((maps (accessible-keymaps keymap)))
3058;; (while maps
3059;; ;; Substitute in this keymap
3060;; (substitute-key-definition olddef newdef (cdr (car maps)))
3061;; (setq maps (cdr maps)))))
3b4a6e27
JB
3062
3063;; Show the name of the file in the buffer.
3064(defun emerge-show-file-name ()
3065 "Displays the name of the file loaded into the current buffer.
3066If the name won't fit on one line, the minibuffer is expanded to hold it,
3067and the command waits for a keystroke from the user. If the keystroke is
3068SPC, it is ignored; if it is anything else, it is processed as a command."
3069 (interactive)
3070 (let ((name (buffer-file-name)))
3071 (or name
3072 (setq name "Buffer has no file name."))
3073 (save-window-excursion
3074 (select-window (minibuffer-window))
784fda4f
JPW
3075 (unwind-protect
3076 (progn
3077 (erase-buffer)
3078 (insert name)
eaf9b564
GM
3079 (while (and (not (pos-visible-in-window-p))
3080 (not (window-full-height-p)))
3081 (enlarge-window 1))
784fda4f
JPW
3082 (let* ((echo-keystrokes 0)
3083 (c (read-event)))
737e3892 3084 (if (not (eq c 32))
784fda4f
JPW
3085 (setq unread-command-events (list c)))))
3086 (erase-buffer)))))
3b4a6e27 3087
c9ec040a 3088;; Improved auto-save file names.
3b4a6e27
JB
3089;; This function fixes many problems with the standard auto-save file names:
3090;; Auto-save files for non-file buffers get put in the default directory
3091;; for the buffer, whether that makes sense or not.
3092;; Auto-save files for file buffers get put in the directory of the file,
3093;; regardless of whether we can write into it or not.
3094;; Auto-save files for non-file buffers don't use the process id, so if a
3095;; user runs more than on Emacs, they can make auto-save files that overwrite
3096;; each other.
3097;; To use this function, do:
3098;; (fset 'make-auto-save-file-name
3099;; (symbol-function 'emerge-make-auto-save-file-name))
3100(defun emerge-make-auto-save-file-name ()
3101 "Return file name to use for auto-saves of current buffer.
737e3892
RS
3102Does not consider `auto-save-visited-file-name';
3103that is checked before calling this function.
3b4a6e27 3104You can redefine this for customization.
737e3892 3105See also `auto-save-file-name-p'."
3b4a6e27
JB
3106 (if buffer-file-name
3107 ;; if buffer has a file, try the format <file directory>/#<file name>#
3108 (let ((f (concat (file-name-directory buffer-file-name)
3109 "#"
3110 (file-name-nondirectory buffer-file-name)
3111 "#")))
3112 (if (file-writable-p f)
3113 ;; the file is writable, so use it
3114 f
3115 ;; the file isn't writable, so use the format
3116 ;; ~/#&<file name>&<hash of directory>#
3117 (concat (getenv "HOME")
3118 "/#&"
3119 (file-name-nondirectory buffer-file-name)
3120 "&"
737e3892 3121 (emerge-hash-string-into-string
3b4a6e27
JB
3122 (file-name-directory buffer-file-name))
3123 "#")))
3124 ;; if buffer has no file, use the format ~/#%<buffer name>%<process id>#
3125 (expand-file-name (concat (getenv "HOME")
3126 "/#%"
3127 ;; quote / into \! and \ into \\
737e3892 3128 (emerge-unslashify-name (buffer-name))
3b4a6e27
JB
3129 "%"
3130 (make-temp-name "")
3131 "#"))))
3132
3133;; Hash a string into five characters more-or-less suitable for use in a file
3134;; name. (Allowed characters are ! through ~, except /.)
737e3892 3135(defun emerge-hash-string-into-string (s)
3b4a6e27
JB
3136 (let ((bins (vector 0 0 0 0 0))
3137 (i 0))
3138 (while (< i (length s))
3139 (aset bins (% i 5) (% (+ (* (aref bins (% i 5)) 35)
3140 (aref s i))
3141 65536))
3142 (setq i (1+ i)))
b05fde66
GM
3143 (mapconcat (lambda (b)
3144 (setq b (+ (% b 93) ?!))
3145 (if (>= b ?/)
3146 (setq b (1+ b)))
3147 (char-to-string b))
3b4a6e27
JB
3148 bins "")))
3149
3150;; Quote any /s in a string by replacing them with \!.
3151;; Also, replace any \s by \\, to make it one-to-one.
737e3892 3152(defun emerge-unslashify-name (s)
3b4a6e27
JB
3153 (let ((limit 0))
3154 (while (string-match "[/\\]" s limit)
3155 (setq s (concat (substring s 0 (match-beginning 0))
3156 (if (string= (substring s (match-beginning 0)
3157 (match-end 0))
3158 "/")
3159 "\\!"
3160 "\\\\")
3161 (substring s (match-end 0))))
3162 (setq limit (1+ (match-end 0)))))
3163 s)
3164
151e4b9c
RS
3165;; Metacharacters that have to be protected from the shell when executing
3166;; a diff/diff3 command.
576bce32
EZ
3167(defcustom emerge-metachars
3168 (if (memq system-type '(ms-dos windows-nt))
3169 "[ \t\"<>|?*^&=]"
3170 "[ \t\n!\"#$&'()*;<=>?[\\^`{|~]")
3171 "Characters that must be quoted when used in a shell command line.
33933d45
AS
3172More precisely, a [...] regexp to match any one such character."
3173 :type 'regexp
3174 :group 'emerge)
151e4b9c
RS
3175
3176;; Quote metacharacters (using \) when executing a diff/diff3 command.
3177(defun emerge-protect-metachars (s)
576bce32
EZ
3178 (if (memq system-type '(ms-dos windows-nt))
3179 (shell-quote-argument s)
3180 (let ((limit 0))
3181 (while (string-match emerge-metachars s limit)
3182 (setq s (concat (substring s 0 (match-beginning 0))
3183 "\\"
3184 (substring s (match-beginning 0))))
3185 (setq limit (1+ (match-end 0)))))
3186 s))
3b4a6e27 3187
737e3892
RS
3188(provide 'emerge)
3189
3190;;; emerge.el ends here