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