Add 2011 to FSF/AIST copyright years.
[bpt/emacs.git] / lisp / smerge-mode.el
CommitLineData
3dac25a9
SM
1;;; smerge-mode.el --- Minor mode to resolve diff3 conflicts
2
869522fb 3;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
5df4f04c 4;; 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
3dac25a9 5
cc1eecfd 6;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
9700a45f 7;; Keywords: tools revision-control merge diff3 cvs conflict
3dac25a9
SM
8
9;; This file is part of GNU Emacs.
10
869522fb 11;; GNU Emacs is free software: you can redistribute it and/or modify
3dac25a9 12;; it under the terms of the GNU General Public License as published by
869522fb
GM
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
3dac25a9
SM
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
869522fb 22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
3dac25a9
SM
23
24;;; Commentary:
25
26;; Provides a lightweight alternative to emerge/ediff.
27;; To use it, simply add to your .emacs the following lines:
28;;
29;; (autoload 'smerge-mode "smerge-mode" nil t)
30;;
31;; you can even have it turned on automatically with the following
32;; piece of code in your .emacs:
33;;
34;; (defun sm-try-smerge ()
35;; (save-excursion
36;; (goto-char (point-min))
37;; (when (re-search-forward "^<<<<<<< " nil t)
38;; (smerge-mode 1))))
1a4914f3 39;; (add-hook 'find-file-hook 'sm-try-smerge t)
3dac25a9 40
f97e9a8a
SM
41;;; Todo:
42
43;; - if requested, ask the user whether he wants to call ediff right away
44
3dac25a9
SM
45;;; Code:
46
a8028e7b
SM
47(eval-when-compile (require 'cl))
48(require 'diff-mode) ;For diff-auto-refine-mode.
3dac25a9
SM
49
50
7f84c46e
RS
51;;; The real definition comes later.
52(defvar smerge-mode)
53
3dac25a9 54(defgroup smerge ()
48d59eda 55 "Minor mode to highlight and resolve diff3 conflicts."
3dac25a9
SM
56 :group 'tools
57 :prefix "smerge-")
58
814838df 59(defcustom smerge-diff-buffer-name "*vc-diff*"
3dac25a9
SM
60 "Buffer name to use for displaying diffs."
61 :group 'smerge
62 :type '(choice
63 (const "*vc-diff*")
64 (const "*cvs-diff*")
65 (const "*smerge-diff*")
66 string))
67
68(defcustom smerge-diff-switches
a7b77977
DL
69 (append '("-d" "-b")
70 (if (listp diff-switches) diff-switches (list diff-switches)))
48d59eda 71 "A list of strings specifying switches to be passed to diff.
3dac25a9
SM
72Used in `smerge-diff-base-mine' and related functions."
73 :group 'smerge
74 :type '(repeat string))
75
f97e9a8a 76(defcustom smerge-auto-leave t
48d59eda 77 "Non-nil means to leave `smerge-mode' when the last conflict is resolved."
f97e9a8a
SM
78 :group 'smerge
79 :type 'boolean)
80
e8bfdf82 81(defface smerge-mine
ea81d57e
DN
82 '((((min-colors 88) (background light))
83 (:foreground "blue1"))
84 (((background light))
b25f5aec 85 (:foreground "blue"))
ea81d57e
DN
86 (((min-colors 88) (background dark))
87 (:foreground "cyan1"))
b25f5aec
MB
88 (((background dark))
89 (:foreground "cyan")))
3dac25a9
SM
90 "Face for your code."
91 :group 'smerge)
c4f6e489 92(define-obsolete-face-alias 'smerge-mine-face 'smerge-mine "22.1")
e8bfdf82 93(defvar smerge-mine-face 'smerge-mine)
3dac25a9 94
e8bfdf82 95(defface smerge-other
b25f5aec
MB
96 '((((background light))
97 (:foreground "darkgreen"))
98 (((background dark))
99 (:foreground "lightgreen")))
3dac25a9
SM
100 "Face for the other code."
101 :group 'smerge)
c4f6e489 102(define-obsolete-face-alias 'smerge-other-face 'smerge-other "22.1")
e8bfdf82 103(defvar smerge-other-face 'smerge-other)
3dac25a9 104
e8bfdf82 105(defface smerge-base
ea81d57e
DN
106 '((((min-colors 88) (background light))
107 (:foreground "red1"))
108 (((background light))
b25f5aec
MB
109 (:foreground "red"))
110 (((background dark))
111 (:foreground "orange")))
3dac25a9
SM
112 "Face for the base code."
113 :group 'smerge)
c4f6e489 114(define-obsolete-face-alias 'smerge-base-face 'smerge-base "22.1")
e8bfdf82 115(defvar smerge-base-face 'smerge-base)
3dac25a9 116
e8bfdf82 117(defface smerge-markers
b25f5aec
MB
118 '((((background light))
119 (:background "grey85"))
120 (((background dark))
121 (:background "grey30")))
3dac25a9
SM
122 "Face for the conflict markers."
123 :group 'smerge)
c4f6e489 124(define-obsolete-face-alias 'smerge-markers-face 'smerge-markers "22.1")
e8bfdf82 125(defvar smerge-markers-face 'smerge-markers)
3dac25a9 126
41796d09
SM
127(defface smerge-refined-change
128 '((t :background "yellow"))
a6022f15
JB
129 "Face used for char-based changes shown by `smerge-refine'."
130 :group 'smerge)
41796d09 131
f97e9a8a 132(easy-mmode-defmap smerge-basic-map
0e86b6b0 133 `(("n" . smerge-next)
3dac25a9 134 ("p" . smerge-prev)
a48402c9 135 ("r" . smerge-resolve)
3dac25a9
SM
136 ("a" . smerge-keep-all)
137 ("b" . smerge-keep-base)
138 ("o" . smerge-keep-other)
139 ("m" . smerge-keep-mine)
140 ("E" . smerge-ediff)
48d59eda 141 ("C" . smerge-combine-with-next)
41796d09 142 ("R" . smerge-refine)
3dac25a9 143 ("\C-m" . smerge-keep-current)
0e86b6b0
SM
144 ("=" . ,(make-sparse-keymap "Diff"))
145 ("=<" "base-mine" . smerge-diff-base-mine)
146 ("=>" "base-other" . smerge-diff-base-other)
147 ("==" "mine-other" . smerge-diff-mine-other))
3dac25a9 148 "The base keymap for `smerge-mode'.")
3dac25a9 149
0e86b6b0 150(defcustom smerge-command-prefix "\C-c^"
3dac25a9
SM
151 "Prefix for `smerge-mode' commands."
152 :group 'smerge
b3fccc27
RS
153 :type '(choice (const :tag "ESC" "\e")
154 (const :tag "C-c ^" "\C-c^" )
155 (const :tag "none" "")
156 string))
3dac25a9 157
f97e9a8a
SM
158(easy-mmode-defmap smerge-mode-map
159 `((,smerge-command-prefix . ,smerge-basic-map))
3dac25a9
SM
160 "Keymap for `smerge-mode'.")
161
7d85a64e
SM
162(defvar smerge-check-cache nil)
163(make-variable-buffer-local 'smerge-check-cache)
164(defun smerge-check (n)
165 (condition-case nil
166 (let ((state (cons (point) (buffer-modified-tick))))
167 (unless (equal (cdr smerge-check-cache) state)
168 (smerge-match-conflict)
169 (setq smerge-check-cache (cons (match-data) state)))
170 (nth (* 2 n) (car smerge-check-cache)))
171 (error nil)))
172
3dac25a9
SM
173(easy-menu-define smerge-mode-menu smerge-mode-map
174 "Menu for `smerge-mode'."
175 '("SMerge"
43e764c9 176 ["Next" smerge-next :help "Go to next conflict"]
394bd1ca 177 ["Previous" smerge-prev :help "Go to previous conflict"]
7d85a64e
SM
178 "--"
179 ["Keep All" smerge-keep-all :help "Keep all three versions"
180 :active (smerge-check 1)]
181 ["Keep Current" smerge-keep-current :help "Use current (at point) version"
182 :active (and (smerge-check 1) (> (smerge-get-current) 0))]
183 "--"
184 ["Revert to Base" smerge-keep-base :help "Revert to base version"
185 :active (smerge-check 2)]
186 ["Keep Other" smerge-keep-other :help "Keep `other' version"
187 :active (smerge-check 3)]
188 ["Keep Yours" smerge-keep-mine :help "Keep your version"
189 :active (smerge-check 1)]
43e764c9
DL
190 "--"
191 ["Diff Base/Mine" smerge-diff-base-mine
7d85a64e
SM
192 :help "Diff `base' and `mine' for current conflict"
193 :active (smerge-check 2)]
43e764c9 194 ["Diff Base/Other" smerge-diff-base-other
7d85a64e
SM
195 :help "Diff `base' and `other' for current conflict"
196 :active (smerge-check 2)]
43e764c9 197 ["Diff Mine/Other" smerge-diff-mine-other
7d85a64e
SM
198 :help "Diff `mine' and `other' for current conflict"
199 :active (smerge-check 1)]
43e764c9
DL
200 "--"
201 ["Invoke Ediff" smerge-ediff
7d85a64e
SM
202 :help "Use Ediff to resolve the conflicts"
203 :active (smerge-check 1)]
204 ["Auto Resolve" smerge-resolve
5bd8d87b
SM
205 :help "Try auto-resolution heuristics"
206 :active (smerge-check 1)]
7d85a64e
SM
207 ["Combine" smerge-combine-with-next
208 :help "Combine current conflict with next"
209 :active (smerge-check 1)]
3dac25a9
SM
210 ))
211
11ece56b
MY
212(easy-menu-define smerge-context-menu nil
213 "Context menu for mine area in `smerge-mode'."
214 '(nil
215 ["Keep Current" smerge-keep-current :help "Use current (at point) version"]
216 ["Kill Current" smerge-kill-current :help "Remove current (at point) version"]
217 ["Keep All" smerge-keep-all :help "Keep all three versions"]
218 "---"
219 ["More..." (popup-menu smerge-mode-menu) :help "Show full SMerge mode menu"]
220 ))
221
3dac25a9
SM
222(defconst smerge-font-lock-keywords
223 '((smerge-find-conflict
f0c1adab 224 (1 smerge-mine-face prepend t)
3dac25a9
SM
225 (2 smerge-base-face prepend t)
226 (3 smerge-other-face prepend t)
0e86b6b0 227 ;; FIXME: `keep' doesn't work right with syntactic fontification.
3dac25a9
SM
228 (0 smerge-markers-face keep)
229 (4 nil t t)
230 (5 nil t t)))
231 "Font lock patterns for `smerge-mode'.")
232
233(defconst smerge-begin-re "^<<<<<<< \\(.*\\)\n")
234(defconst smerge-end-re "^>>>>>>> .*\n")
235(defconst smerge-base-re "^||||||| .*\n")
236(defconst smerge-other-re "^=======\n")
237
238(defvar smerge-conflict-style nil
239 "Keep track of which style of conflict is in use.
240Can be nil if the style is undecided, or else:
241- `diff3-E'
242- `diff3-A'")
243
244;; Compiler pacifiers
8f6cea29
DL
245(defvar font-lock-mode)
246(defvar font-lock-keywords)
3dac25a9
SM
247
248;;;;
249;;;; Actual code
250;;;;
251
f97e9a8a 252;; Define smerge-next and smerge-prev
2daf4bc6 253(easy-mmode-define-navigation smerge smerge-begin-re "conflict" nil nil
7381be9d 254 (if diff-auto-refine-mode
2daf4bc6 255 (condition-case nil (smerge-refine) (error nil))))
3dac25a9
SM
256
257(defconst smerge-match-names ["conflict" "mine" "base" "other"])
258
259(defun smerge-ensure-match (n)
260 (unless (match-end n)
376166e6 261 (error "No `%s'" (aref smerge-match-names n))))
3dac25a9 262
f97e9a8a
SM
263(defun smerge-auto-leave ()
264 (when (and smerge-auto-leave
265 (save-excursion (goto-char (point-min))
266 (not (re-search-forward smerge-begin-re nil t))))
48d59eda
SM
267 (when (and (listp buffer-undo-list) smerge-mode)
268 (push (list 'apply 'smerge-mode 1) buffer-undo-list))
f97e9a8a 269 (smerge-mode -1)))
f1180544 270
f97e9a8a 271
3dac25a9 272(defun smerge-keep-all ()
5bd8d87b 273 "Concatenate all versions."
3dac25a9
SM
274 (interactive)
275 (smerge-match-conflict)
5bd8d87b
SM
276 (let ((mb2 (or (match-beginning 2) (point-max)))
277 (me2 (or (match-end 2) (point-min))))
278 (delete-region (match-end 3) (match-end 0))
279 (delete-region (max me2 (match-end 1)) (match-beginning 3))
280 (if (and (match-end 2) (/= (match-end 1) (match-end 3)))
281 (delete-region (match-end 1) (match-beginning 2)))
282 (delete-region (match-beginning 0) (min (match-beginning 1) mb2))
283 (smerge-auto-leave)))
284
285(defun smerge-keep-n (n)
41796d09 286 (smerge-remove-props (match-beginning 0) (match-end 0))
5bd8d87b
SM
287 ;; We used to use replace-match, but that did not preserve markers so well.
288 (delete-region (match-end n) (match-end 0))
289 (delete-region (match-beginning 0) (match-beginning n)))
3dac25a9 290
814838df
SM
291(defun smerge-combine-with-next ()
292 "Combine the current conflict with the next one."
02dfeba8
SM
293 ;; `smerge-auto-combine' relies on the finish position (at the beginning
294 ;; of the closing marker).
814838df
SM
295 (interactive)
296 (smerge-match-conflict)
297 (let ((ends nil))
298 (dolist (i '(3 2 1 0))
299 (push (if (match-end i) (copy-marker (match-end i) t)) ends))
300 (setq ends (apply 'vector ends))
301 (goto-char (aref ends 0))
302 (if (not (re-search-forward smerge-begin-re nil t))
303 (error "No next conflict")
304 (smerge-match-conflict)
305 (let ((match-data (mapcar (lambda (m) (if m (copy-marker m)))
306 (match-data))))
307 ;; First copy the in-between text in each alternative.
308 (dolist (i '(1 2 3))
309 (when (aref ends i)
310 (goto-char (aref ends i))
311 (insert-buffer-substring (current-buffer)
312 (aref ends 0) (car match-data))))
313 (delete-region (aref ends 0) (car match-data))
314 ;; Then move the second conflict's alternatives into the first.
315 (dolist (i '(1 2 3))
316 (set-match-data match-data)
317 (when (and (aref ends i) (match-end i))
318 (goto-char (aref ends i))
319 (insert-buffer-substring (current-buffer)
320 (match-beginning i) (match-end i))))
321 (delete-region (car match-data) (cadr match-data))
322 ;; Free the markers.
323 (dolist (m match-data) (if m (move-marker m nil)))
324 (mapc (lambda (m) (if m (move-marker m nil))) ends)))))
325
02dfeba8
SM
326(defvar smerge-auto-combine-max-separation 2
327 "Max number of lines between conflicts that should be combined.")
328
329(defun smerge-auto-combine ()
330 "Automatically combine conflicts that are near each other."
331 (interactive)
332 (save-excursion
333 (goto-char (point-min))
334 (while (smerge-find-conflict)
335 ;; 2 is 1 (default) + 1 (the begin markers).
336 (while (save-excursion
337 (smerge-find-conflict
338 (line-beginning-position
339 (+ 2 smerge-auto-combine-max-separation))))
340 (forward-line -1) ;Go back inside the conflict.
341 (smerge-combine-with-next)
342 (forward-line 1) ;Move past the end of the conflict.
343 ))))
344
a48402c9
SM
345(defvar smerge-resolve-function
346 (lambda () (error "Don't know how to resolve"))
347 "Mode-specific merge function.
de689511
SM
348The function is called with zero or one argument (non-nil if the resolution
349function should only apply safe heuristics) and with the match data set
a48402c9 350according to `smerge-match-conflict'.")
48d59eda 351(add-to-list 'debug-ignored-errors "Don't know how to resolve")
a48402c9 352
11ece56b
MY
353(defvar smerge-text-properties
354 `(help-echo "merge conflict: mouse-3 shows a menu"
355 ;; mouse-face highlight
356 keymap (keymap (down-mouse-3 . smerge-popup-context-menu))))
357
41796d09
SM
358(defun smerge-remove-props (beg end)
359 (remove-overlays beg end 'smerge 'refine)
91773964
SM
360 (remove-overlays beg end 'smerge 'conflict)
361 ;; Now that we use overlays rather than text-properties, this function
362 ;; does not cause refontification any more. It can be seen very clearly
363 ;; in buffers where jit-lock-contextually is not t, in which case deleting
364 ;; the "<<<<<<< foobar" leading line leaves the rest of the conflict
365 ;; highlighted as if it were still a valid conflict. Note that in many
366 ;; important cases (such as the previous example) we're actually called
367 ;; during font-locking so inhibit-modification-hooks is non-nil, so we
368 ;; can't just modify the buffer and expect font-lock to be triggered as in:
369 ;; (put-text-property beg end 'smerge-force-highlighting nil)
755e0210
SM
370 (with-silent-modifications
371 (remove-text-properties beg end '(fontified nil))))
11ece56b
MY
372
373(defun smerge-popup-context-menu (event)
374 "Pop up the Smerge mode context menu under mouse."
375 (interactive "e")
376 (if (and smerge-mode
65114860 377 (save-excursion (posn-set-point (event-end event)) (smerge-check 1)))
11ece56b 378 (progn
65114860 379 (posn-set-point (event-end event))
5bd8d87b
SM
380 (smerge-match-conflict)
381 (let ((i (smerge-get-current))
382 o)
383 (if (<= i 0)
384 ;; Out of range
385 (popup-menu smerge-mode-menu)
386 ;; Install overlay.
3b0af402 387 (setq o (make-overlay (match-beginning i) (match-end i)))
5bd8d87b
SM
388 (unwind-protect
389 (progn
390 (overlay-put o 'face 'highlight)
391 (sit-for 0) ;Display the new highlighting.
392 (popup-menu smerge-context-menu))
393 ;; Delete overlay.
394 (delete-overlay o)))))
11ece56b
MY
395 ;; There's no conflict at point, the text-props are just obsolete.
396 (save-excursion
397 (let ((beg (re-search-backward smerge-end-re nil t))
5bd8d87b
SM
398 (end (re-search-forward smerge-begin-re nil t)))
399 (smerge-remove-props (or beg (point-min)) (or end (point-max)))
400 (push event unread-command-events)))))
11ece56b 401
56d707f1
SM
402(defun smerge-apply-resolution-patch (buf m0b m0e m3b m3e &optional m2b)
403 "Replace the conflict with a bunch of subconflicts.
404BUF contains a plain diff between match-1 and match-3."
405 (let ((line 1)
406 (textbuf (current-buffer))
407 (name1 (progn (goto-char m0b)
408 (buffer-substring (+ (point) 8) (line-end-position))))
409 (name2 (when m2b (goto-char m2b) (forward-line -1)
410 (buffer-substring (+ (point) 8) (line-end-position))))
411 (name3 (progn (goto-char m0e) (forward-line -1)
412 (buffer-substring (+ (point) 8) (line-end-position)))))
413 (smerge-remove-props m0b m0e)
414 (delete-region m3e m0e)
415 (delete-region m0b m3b)
416 (setq m3b m0b)
417 (setq m3e (- m3e (- m3b m0b)))
418 (goto-char m3b)
419 (with-current-buffer buf
420 (goto-char (point-min))
421 (while (not (eobp))
422 (if (not (looking-at "\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)?\\([acd]\\)\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)?$"))
423 (error "Unexpected patch hunk header: %s"
424 (buffer-substring (point) (line-end-position)))
425 (let* ((op (char-after (match-beginning 3)))
426 (startline (+ (string-to-number (match-string 1))
427 ;; No clue why this is the way it is, but line
428 ;; numbers seem to be off-by-one for `a' ops.
429 (if (eq op ?a) 1 0)))
430 (endline (if (eq op ?a) startline
431 (1+ (if (match-end 2)
432 (string-to-number (match-string 2))
433 startline))))
434 (lines (- endline startline))
435 (otherlines (cond
436 ((eq op ?d) nil)
437 ((null (match-end 5)) 1)
438 (t (- (string-to-number (match-string 5))
439 (string-to-number (match-string 4)) -1))))
440 othertext)
441 (forward-line 1) ;Skip header.
442 (forward-line lines) ;Skip deleted text.
443 (if (eq op ?c) (forward-line 1)) ;Skip separator.
444 (setq othertext
445 (if (null otherlines) ""
446 (let ((pos (point)))
447 (dotimes (i otherlines) (delete-char 2) (forward-line 1))
448 (buffer-substring pos (point)))))
449 (with-current-buffer textbuf
450 (forward-line (- startline line))
451 (insert "<<<<<<< " name1 "\n" othertext
d5c14937 452 (if name2 (concat "||||||| " name2 "\n") "")
56d707f1
SM
453 "=======\n")
454 (forward-line lines)
455 (insert ">>>>>>> " name3 "\n")
456 (setq line endline))))))))
457
de689511 458(defun smerge-resolve (&optional safe)
a48402c9
SM
459 "Resolve the conflict at point intelligently.
460This relies on mode-specific knowledge and thus only works in
461some major modes. Uses `smerge-resolve-function' to do the actual work."
462 (interactive)
463 (smerge-match-conflict)
f57b45cf 464 (smerge-remove-props (match-beginning 0) (match-end 0))
56d707f1
SM
465 (let ((md (match-data))
466 (m0b (match-beginning 0))
467 (m1b (match-beginning 1))
468 (m2b (match-beginning 2))
469 (m3b (match-beginning 3))
470 (m0e (match-end 0))
471 (m1e (match-end 1))
472 (m2e (match-end 2))
473 (m3e (match-end 3))
474 (buf (generate-new-buffer " *smerge*"))
475 m b o)
476 (unwind-protect
477 (progn
478 (cond
479 ;; Trivial diff3 -A non-conflicts.
480 ((and (eq (match-end 1) (match-end 3))
481 (eq (match-beginning 1) (match-beginning 3)))
482 (smerge-keep-n 3))
483 ;; Mode-specific conflict resolution.
484 ((condition-case nil
485 (atomic-change-group
486 (if safe
487 (funcall smerge-resolve-function safe)
488 (funcall smerge-resolve-function))
489 t)
490 (error nil))
491 ;; Nothing to do: the resolution function has done it already.
492 nil)
0e05d8fc
SM
493 ;; Non-conflict.
494 ((and (eq m1e m3e) (eq m1b m3b))
495 (set-match-data md) (smerge-keep-n 3))
56d707f1
SM
496 ;; Refine a 2-way conflict using "diff -b".
497 ;; In case of a 3-way conflict with an empty base
498 ;; (i.e. 2 conflicting additions), we do the same, presuming
499 ;; that the 2 additions should be somehow merged rather
500 ;; than concatenated.
0e05d8fc
SM
501 ((let ((lines (count-lines m3b m3e)))
502 (setq m (make-temp-file "smm"))
503 (write-region m1b m1e m nil 'silent)
504 (setq o (make-temp-file "smo"))
505 (write-region m3b m3e o nil 'silent)
506 (not (or (eq m1b m1e) (eq m3b m3e)
507 (and (not (zerop (call-process diff-command
508 nil buf nil "-b" o m)))
509 ;; TODO: We don't know how to do the refinement
510 ;; if there's a non-empty ancestor and m1 and m3
511 ;; aren't just plain equal.
512 m2b (not (eq m2b m2e)))
56d707f1
SM
513 (with-current-buffer buf
514 (goto-char (point-min))
515 ;; Make sure there's some refinement.
516 (looking-at
517 (concat "1," (number-to-string lines) "c"))))))
518 (smerge-apply-resolution-patch buf m0b m0e m3b m3e m2b))
0e05d8fc
SM
519 ;; "Mere whitespace changes" conflicts.
520 ((when m2e
521 (setq b (make-temp-file "smb"))
522 (write-region m2b m2e b nil 'silent)
523 (with-current-buffer buf (erase-buffer))
524 ;; Only minor whitespace changes made locally.
525 ;; BEWARE: pass "-c" 'cause the output is reused in the next test.
526 (zerop (call-process diff-command nil buf nil "-bc" b m)))
527 (set-match-data md)
528 (smerge-keep-n 3))
56d707f1
SM
529 ;; Try "diff -b BASE MINE | patch OTHER".
530 ((when (and (not safe) m2e b
531 ;; If the BASE is empty, this would just concatenate
532 ;; the two, which is rarely right.
533 (not (eq m2b m2e)))
0e05d8fc 534 ;; BEWARE: we're using here the patch of the previous test.
56d707f1
SM
535 (with-current-buffer buf
536 (zerop (call-process-region
537 (point-min) (point-max) "patch" t nil nil
538 "-r" "/dev/null" "--no-backup-if-mismatch"
539 "-fl" o))))
540 (save-restriction
541 (narrow-to-region m0b m0e)
542 (smerge-remove-props m0b m0e)
543 (insert-file-contents o nil nil nil t)))
544 ;; Try "diff -b BASE OTHER | patch MINE".
545 ((when (and (not safe) m2e b
546 ;; If the BASE is empty, this would just concatenate
547 ;; the two, which is rarely right.
548 (not (eq m2b m2e)))
549 (write-region m3b m3e o nil 'silent)
550 (call-process diff-command nil buf nil "-bc" b o)
551 (with-current-buffer buf
552 (zerop (call-process-region
553 (point-min) (point-max) "patch" t nil nil
554 "-r" "/dev/null" "--no-backup-if-mismatch"
555 "-fl" m))))
556 (save-restriction
557 (narrow-to-region m0b m0e)
558 (smerge-remove-props m0b m0e)
559 (insert-file-contents m nil nil nil t)))
560 (t
561 (error "Don't know how to resolve"))))
562 (if (buffer-name buf) (kill-buffer buf))
563 (if m (delete-file m))
564 (if b (delete-file b))
565 (if o (delete-file o))))
a48402c9
SM
566 (smerge-auto-leave))
567
de689511
SM
568(defun smerge-resolve-all ()
569 "Perform automatic resolution on all conflicts."
570 (interactive)
571 (save-excursion
572 (goto-char (point-min))
573 (while (re-search-forward smerge-begin-re nil t)
574 (condition-case nil
575 (progn
576 (smerge-match-conflict)
577 (smerge-resolve 'safe))
578 (error nil)))))
579
580(defun smerge-batch-resolve ()
581 ;; command-line-args-left is what is left of the command line.
582 (if (not noninteractive)
583 (error "`smerge-batch-resolve' is to be used only with -batch"))
584 (while command-line-args-left
585 (let ((file (pop command-line-args-left)))
2daf4bc6
SM
586 (if (string-match "\\.rej\\'" file)
587 ;; .rej files should never contain diff3 markers, on the other hand,
588 ;; in Arch, .rej files are sometimes used to indicate that the
589 ;; main file has diff3 markers. So you can pass **/*.rej and
590 ;; it will DTRT.
591 (setq file (substring file 0 (match-beginning 0))))
de689511
SM
592 (message "Resolving conflicts in %s..." file)
593 (when (file-readable-p file)
594 (with-current-buffer (find-file-noselect file)
595 (smerge-resolve-all)
596 (save-buffer)
597 (kill-buffer (current-buffer)))))))
598
3dac25a9
SM
599(defun smerge-keep-base ()
600 "Revert to the base version."
601 (interactive)
602 (smerge-match-conflict)
603 (smerge-ensure-match 2)
5bd8d87b 604 (smerge-keep-n 2)
f97e9a8a 605 (smerge-auto-leave))
3dac25a9
SM
606
607(defun smerge-keep-other ()
608 "Use \"other\" version."
609 (interactive)
610 (smerge-match-conflict)
611 ;;(smerge-ensure-match 3)
5bd8d87b 612 (smerge-keep-n 3)
f97e9a8a 613 (smerge-auto-leave))
3dac25a9
SM
614
615(defun smerge-keep-mine ()
616 "Keep your version."
617 (interactive)
618 (smerge-match-conflict)
619 ;;(smerge-ensure-match 1)
5bd8d87b 620 (smerge-keep-n 1)
f97e9a8a 621 (smerge-auto-leave))
3dac25a9 622
7d85a64e 623(defun smerge-get-current ()
3dac25a9
SM
624 (let ((i 3))
625 (while (or (not (match-end i))
626 (< (point) (match-beginning i))
627 (>= (point) (match-end i)))
628 (decf i))
7d85a64e
SM
629 i))
630
631(defun smerge-keep-current ()
632 "Use the current (under the cursor) version."
633 (interactive)
634 (smerge-match-conflict)
635 (let ((i (smerge-get-current)))
3dac25a9 636 (if (<= i 0) (error "Not inside a version")
5bd8d87b 637 (smerge-keep-n i)
f97e9a8a 638 (smerge-auto-leave))))
3dac25a9 639
11ece56b
MY
640(defun smerge-kill-current ()
641 "Remove the current (under the cursor) version."
642 (interactive)
643 (smerge-match-conflict)
644 (let ((i (smerge-get-current)))
645 (if (<= i 0) (error "Not inside a version")
5bd8d87b
SM
646 (let ((left nil))
647 (dolist (n '(3 2 1))
648 (if (and (match-end n) (/= (match-end n) (match-end i)))
649 (push n left)))
650 (if (and (cdr left)
651 (/= (match-end (car left)) (match-end (cadr left))))
652 (ding) ;We don't know how to do that.
653 (smerge-keep-n (car left))
654 (smerge-auto-leave))))))
11ece56b 655
3dac25a9
SM
656(defun smerge-diff-base-mine ()
657 "Diff 'base' and 'mine' version in current conflict region."
658 (interactive)
659 (smerge-diff 2 1))
660
661(defun smerge-diff-base-other ()
662 "Diff 'base' and 'other' version in current conflict region."
663 (interactive)
664 (smerge-diff 2 3))
665
666(defun smerge-diff-mine-other ()
667 "Diff 'mine' and 'other' version in current conflict region."
668 (interactive)
669 (smerge-diff 1 3))
670
671(defun smerge-match-conflict ()
672 "Get info about the conflict. Puts the info in the `match-data'.
673The submatches contain:
674 0: the whole conflict.
675 1: your code.
676 2: the base code.
677 3: other code.
678An error is raised if not inside a conflict."
679 (save-excursion
680 (condition-case nil
681 (let* ((orig-point (point))
682
683 (_ (forward-line 1))
684 (_ (re-search-backward smerge-begin-re))
685
686 (start (match-beginning 0))
687 (mine-start (match-end 0))
a48402c9 688 (filename (or (match-string 1) ""))
3dac25a9
SM
689
690 (_ (re-search-forward smerge-end-re))
691 (_ (assert (< orig-point (match-end 0))))
f1180544 692
3dac25a9
SM
693 (other-end (match-beginning 0))
694 (end (match-end 0))
695
696 (_ (re-search-backward smerge-other-re start))
697
698 (mine-end (match-beginning 0))
699 (other-start (match-end 0))
700
701 base-start base-end)
702
703 ;; handle the various conflict styles
704 (cond
9f0c286d
SM
705 ((save-excursion
706 (goto-char mine-start)
2a3d70d4 707 (re-search-forward smerge-begin-re end t))
b4dc7d98 708 ;; There's a nested conflict and we're after the beginning
9f0c286d 709 ;; of the outer one but before the beginning of the inner one.
48d59eda
SM
710 ;; Of course, maybe this is not a nested conflict but in that
711 ;; case it can only be something nastier that we don't know how
712 ;; to handle, so may as well arbitrarily decide to treat it as
713 ;; a nested conflict. --Stef
9f0c286d
SM
714 (error "There is a nested conflict"))
715
3dac25a9
SM
716 ((re-search-backward smerge-base-re start t)
717 ;; a 3-parts conflict
718 (set (make-local-variable 'smerge-conflict-style) 'diff3-A)
719 (setq base-end mine-end)
720 (setq mine-end (match-beginning 0))
721 (setq base-start (match-end 0)))
722
11ece56b
MY
723 ((string= filename (file-name-nondirectory
724 (or buffer-file-name "")))
725 ;; a 2-parts conflict
726 (set (make-local-variable 'smerge-conflict-style) 'diff3-E))
727
728 ((and (not base-start)
729 (or (eq smerge-conflict-style 'diff3-A)
730 (equal filename "ANCESTOR")
731 (string-match "\\`[.0-9]+\\'" filename)))
732 ;; a same-diff conflict
733 (setq base-start mine-start)
734 (setq base-end mine-end)
735 (setq mine-start other-start)
736 (setq mine-end other-end)))
737
3dac25a9
SM
738 (store-match-data (list start end
739 mine-start mine-end
740 base-start base-end
741 other-start other-end
742 (when base-start (1- base-start)) base-start
743 (1- other-start) other-start))
744 t)
e29f823e 745 (search-failed (error "Point not in conflict region")))))
3dac25a9 746
0778a62f
SM
747(add-to-list 'debug-ignored-errors "Point not in conflict region")
748
48d59eda
SM
749(defun smerge-conflict-overlay (pos)
750 "Return the conflict overlay at POS if any."
751 (let ((ols (overlays-at pos))
752 conflict)
753 (dolist (ol ols)
754 (if (and (eq (overlay-get ol 'smerge) 'conflict)
755 (> (overlay-end ol) pos))
756 (setq conflict ol)))
757 conflict))
758
3dac25a9
SM
759(defun smerge-find-conflict (&optional limit)
760 "Find and match a conflict region. Intended as a font-lock MATCHER.
761The submatches are the same as in `smerge-match-conflict'.
48d59eda
SM
762Returns non-nil if a match is found between point and LIMIT.
763Point is moved to the end of the conflict."
764 (let ((found nil)
765 (pos (point))
766 conflict)
767 ;; First check to see if point is already inside a conflict, using
768 ;; the conflict overlays.
769 (while (and (not found) (setq conflict (smerge-conflict-overlay pos)))
770 ;; Check the overlay's validity and kill it if it's out of date.
771 (condition-case nil
772 (progn
773 (goto-char (overlay-start conflict))
774 (smerge-match-conflict)
775 (goto-char (match-end 0))
776 (if (<= (point) pos)
777 (error "Matching backward!")
778 (setq found t)))
779 (error (smerge-remove-props
780 (overlay-start conflict) (overlay-end conflict))
781 (goto-char pos))))
782 ;; If we're not already inside a conflict, look for the next conflict
783 ;; and add/update its overlay.
784 (while (and (not found) (re-search-forward smerge-begin-re limit t))
785 (condition-case nil
786 (progn
787 (smerge-match-conflict)
788 (goto-char (match-end 0))
789 (let ((conflict (smerge-conflict-overlay (1- (point)))))
790 (if conflict
791 ;; Update its location, just in case it got messed up.
792 (move-overlay conflict (match-beginning 0) (match-end 0))
793 (setq conflict (make-overlay (match-beginning 0) (match-end 0)
794 nil 'front-advance nil))
795 (overlay-put conflict 'evaporate t)
796 (overlay-put conflict 'smerge 'conflict)
797 (let ((props smerge-text-properties))
798 (while props
799 (overlay-put conflict (pop props) (pop props))))))
800 (setq found t))
801 (error nil)))
802 found))
3dac25a9 803
cd62539f
SM
804;;; Refined change highlighting
805
806(defvar smerge-refine-forward-function 'smerge-refine-forward
807 "Function used to determine an \"atomic\" element.
808You can set it to `forward-char' to get char-level granularity.
809Its behavior has mainly two restrictions:
810- if this function encounters a newline, it's important that it stops right
811 after the newline.
812 This only matters if `smerge-refine-ignore-whitespace' is nil.
813- it needs to be unaffected by changes performed by the `preproc' argument
814 to `smerge-refine-subst'.
815 This only matters if `smerge-refine-weight-hack' is nil.")
816
817(defvar smerge-refine-ignore-whitespace t
818 "If non-nil,Indicate that smerge-refine should try to ignore change in whitespace.")
819
820(defvar smerge-refine-weight-hack t
821 "If non-nil, pass to diff as many lines as there are chars in the region.
822I.e. each atomic element (e.g. word) will be copied as many times (on different
823lines) as it has chars. This has 2 advantages:
824- if `diff' tries to minimize the number *lines* (rather than chars)
825 added/removed, this adjust the weights so that adding/removing long
826 symbols is considered correspondingly more costly.
827- `smerge-refine-forward-function' only needs to be called when chopping up
828 the regions, and `forward-char' can be used afterwards.
829It has the following disadvantages:
830- cannot use `diff -w' because the weighting causes added spaces in a line
831 to be represented as added copies of some line, so `diff -w' can't do the
832 right thing any more.
833- may in degenerate cases take a 1KB input region and turn it into a 1MB
834 file to pass to diff.")
835
836(defun smerge-refine-forward (n)
837 (let ((case-fold-search nil)
838 (re "[[:upper:]]?[[:lower:]]+\\|[[:upper:]]+\\|[[:digit:]]+\\|.\\|\n"))
839 (when (and smerge-refine-ignore-whitespace
840 ;; smerge-refine-weight-hack causes additional spaces to
841 ;; appear as additional lines as well, so even if diff ignore
842 ;; whitespace changes, it'll report added/removed lines :-(
843 (not smerge-refine-weight-hack))
844 (setq re (concat "[ \t]*\\(?:" re "\\)")))
845 (dotimes (i n)
846 (unless (looking-at re) (error "Smerge refine internal error"))
847 (goto-char (match-end 0)))))
848
9f2e22a0
SM
849(defun smerge-refine-chopup-region (beg end file &optional preproc)
850 "Chopup the region into small elements, one per line.
851Save the result into FILE.
852If non-nil, PREPROC is called with no argument in a buffer that contains
853a copy of the text, just before chopping it up. It can be used to replace
854chars to try and eliminate some spurious differences."
cd62539f
SM
855 ;; We used to chop up char-by-char rather than word-by-word like ediff
856 ;; does. It had the benefit of simplicity and very fine results, but it
857 ;; often suffered from problem that diff would find correlations where
858 ;; there aren't any, so the resulting "change" didn't make much sense.
859 ;; You can still get this behavior by setting
860 ;; `smerge-refine-forward-function' to `forward-char'.
41796d09
SM
861 (let ((buf (current-buffer)))
862 (with-temp-buffer
863 (insert-buffer-substring buf beg end)
9f2e22a0 864 (when preproc (goto-char (point-min)) (funcall preproc))
cd62539f
SM
865 (when smerge-refine-ignore-whitespace
866 ;; It doesn't make much of a difference for diff-fine-highlight
867 ;; because we still have the _/+/</>/! prefix anyway. Can still be
868 ;; useful in other circumstances.
869 (subst-char-in-region (point-min) (point-max) ?\n ?\s))
41796d09
SM
870 (goto-char (point-min))
871 (while (not (eobp))
cd62539f
SM
872 (funcall smerge-refine-forward-function 1)
873 (let ((s (if (prog2 (forward-char -1) (bolp) (forward-char 1))
874 nil
875 (buffer-substring (line-beginning-position) (point)))))
876 ;; We add \n after each char except after \n, so we get
877 ;; one line per text char, where each line contains
878 ;; just one char, except for \n chars which are
879 ;; represented by the empty line.
880 (unless (eq (char-before) ?\n) (insert ?\n))
881 ;; HACK ALERT!!
882 (if smerge-refine-weight-hack
883 (dotimes (i (1- (length s))) (insert s "\n")))))
884 (unless (bolp) (error "Smerge refine internal error"))
41796d09
SM
885 (let ((coding-system-for-write 'emacs-mule))
886 (write-region (point-min) (point-max) file nil 'nomessage)))))
887
9f2e22a0 888(defun smerge-refine-highlight-change (buf beg match-num1 match-num2 props)
cd62539f
SM
889 (with-current-buffer buf
890 (goto-char beg)
891 (let* ((startline (- (string-to-number match-num1) 1))
892 (beg (progn (funcall (if smerge-refine-weight-hack
893 'forward-char
894 smerge-refine-forward-function)
895 startline)
896 (point)))
897 (end (progn (funcall (if smerge-refine-weight-hack
898 'forward-char
899 smerge-refine-forward-function)
900 (if match-num2
901 (- (string-to-number match-num2)
902 startline)
903 1))
904 (point))))
905 (when smerge-refine-ignore-whitespace
906 (skip-chars-backward " \t\n" beg) (setq end (point))
907 (goto-char beg)
908 (skip-chars-forward " \t\n" end) (setq beg (point)))
909 (when (> end beg)
910 (let ((ol (make-overlay
911 beg end nil
912 ;; Make them tend to shrink rather than spread when editing.
913 'front-advance nil)))
914 (overlay-put ol 'evaporate t)
915 (dolist (x props) (overlay-put ol (car x) (cdr x)))
916 ol)))))
9f2e22a0
SM
917
918(defun smerge-refine-subst (beg1 end1 beg2 end2 props &optional preproc)
919 "Show fine differences in the two regions BEG1..END1 and BEG2..END2.
920PROPS is an alist of properties to put (via overlays) on the changes.
921If non-nil, PREPROC is called with no argument in a buffer that contains
922a copy of a region, just before preparing it to for `diff'. It can be used to
923replace chars to try and eliminate some spurious differences."
924 (let* ((buf (current-buffer))
cd62539f 925 (pos (point))
9f2e22a0
SM
926 (file1 (make-temp-file "diff1"))
927 (file2 (make-temp-file "diff2")))
41796d09 928 ;; Chop up regions into smaller elements and save into files.
9f2e22a0
SM
929 (smerge-refine-chopup-region beg1 end1 file1 preproc)
930 (smerge-refine-chopup-region beg2 end2 file2 preproc)
41796d09
SM
931
932 ;; Call diff on those files.
933 (unwind-protect
934 (with-temp-buffer
935 (let ((coding-system-for-read 'emacs-mule))
cd62539f
SM
936 (call-process diff-command nil t nil
937 (if (and smerge-refine-ignore-whitespace
938 (not smerge-refine-weight-hack))
f56f00fa
SM
939 ;; Pass -a so diff treats it as a text file even
940 ;; if it contains \0 and such.
941 ;; Pass -d so as to get the smallest change, but
942 ;; also and more importantly because otherwise it
943 ;; may happen that diff doesn't behave like
944 ;; smerge-refine-weight-hack expects it to.
945 ;; See http://thread.gmane.org/gmane.emacs.devel/82685.
946 "-awd" "-ad")
cd62539f 947 file1 file2))
41796d09
SM
948 ;; Process diff's output.
949 (goto-char (point-min))
cd62539f
SM
950 (let ((last1 nil)
951 (last2 nil))
952 (while (not (eobp))
953 (if (not (looking-at "\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)?\\([acd]\\)\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)?$"))
954 (error "Unexpected patch hunk header: %s"
955 (buffer-substring (point) (line-end-position))))
956 (let ((op (char-after (match-beginning 3)))
957 (m1 (match-string 1))
958 (m2 (match-string 2))
959 (m4 (match-string 4))
960 (m5 (match-string 5)))
41796d09 961 (when (memq op '(?d ?c))
cd62539f
SM
962 (setq last1
963 (smerge-refine-highlight-change buf beg1 m1 m2 props)))
41796d09 964 (when (memq op '(?a ?c))
cd62539f
SM
965 (setq last2
966 (smerge-refine-highlight-change buf beg2 m4 m5 props))))
41796d09
SM
967 (forward-line 1) ;Skip hunk header.
968 (and (re-search-forward "^[0-9]" nil 'move) ;Skip hunk body.
cd62539f
SM
969 (goto-char (match-beginning 0))))
970 ;; (assert (or (null last1) (< (overlay-start last1) end1)))
971 ;; (assert (or (null last2) (< (overlay-start last2) end2)))
972 (if smerge-refine-weight-hack
973 (progn
974 ;; (assert (or (null last1) (<= (overlay-end last1) end1)))
975 ;; (assert (or (null last2) (<= (overlay-end last2) end2)))
976 )
977 ;; smerge-refine-forward-function when calling in chopup may
978 ;; have stopped because it bumped into EOB whereas in
979 ;; smerge-refine-weight-hack it may go a bit further.
980 (if (and last1 (> (overlay-end last1) end1))
981 (move-overlay last1 (overlay-start last1) end1))
982 (if (and last2 (> (overlay-end last2) end2))
983 (move-overlay last2 (overlay-start last2) end2))
984 )))
985 (goto-char pos)
41796d09
SM
986 (delete-file file1)
987 (delete-file file2))))
988
2fa42bb7
SM
989(defun smerge-refine (&optional part)
990 "Highlight the words of the conflict that are different.
991For 3-way conflicts, highlights only 2 of the 3 parts.
992A numeric argument PART can be used to specify which 2 parts;
993repeating the command will highlight other 2 parts."
994 (interactive
995 (if (integerp current-prefix-arg) (list current-prefix-arg)
996 (smerge-match-conflict)
997 (let* ((prop (get-text-property (match-beginning 0) 'smerge-refine-part))
998 (part (if (and (consp prop)
999 (eq (buffer-chars-modified-tick) (car prop)))
1000 (cdr prop))))
1001 ;; If already highlighted, cycle.
1002 (list (if (integerp part) (1+ (mod part 3)))))))
1003
1004 (if (and (integerp part) (or (< part 1) (> part 3)))
1005 (error "No conflict part nb %s" part))
9f2e22a0
SM
1006 (smerge-match-conflict)
1007 (remove-overlays (match-beginning 0) (match-end 0) 'smerge 'refine)
2fa42bb7
SM
1008 ;; Ignore `part' if not applicable, and default it if not provided.
1009 (setq part (cond ((null (match-end 2)) 2)
1010 ((eq (match-end 1) (match-end 3)) 1)
1011 ((integerp part) part)
1012 (t 2)))
1013 (let ((n1 (if (eq part 1) 2 1))
1014 (n2 (if (eq part 3) 2 3)))
1015 (smerge-ensure-match n1)
1016 (smerge-ensure-match n2)
755e0210
SM
1017 (with-silent-modifications
1018 (put-text-property (match-beginning 0) (1+ (match-beginning 0))
1019 'smerge-refine-part
1020 (cons (buffer-chars-modified-tick) part)))
30e68410 1021 (smerge-refine-subst (match-beginning n1) (match-end n1)
2fa42bb7 1022 (match-beginning n2) (match-end n2)
30e68410
SM
1023 '((smerge . refine)
1024 (face . smerge-refined-change)))))
9f2e22a0 1025
3dac25a9
SM
1026(defun smerge-diff (n1 n2)
1027 (smerge-match-conflict)
1028 (smerge-ensure-match n1)
1029 (smerge-ensure-match n2)
1030 (let ((name1 (aref smerge-match-names n1))
1031 (name2 (aref smerge-match-names n2))
e29f823e
SM
1032 ;; Read them before the match-data gets clobbered.
1033 (beg1 (match-beginning n1))
1034 (end1 (match-end n1))
1035 (beg2 (match-beginning n2))
1036 (end2 (match-end n2))
3dac25a9 1037 (file1 (make-temp-file "smerge1"))
d73aed13
SM
1038 (file2 (make-temp-file "smerge2"))
1039 (dir default-directory)
48d59eda
SM
1040 (file (if buffer-file-name (file-relative-name buffer-file-name)))
1041 ;; We would want to use `emacs-mule-unix' for read&write, but we
1042 ;; bump into problems with the coding-system used by diff to write
1043 ;; the file names and the time stamps in the header.
1044 ;; `buffer-file-coding-system' is not always correct either, but if
1045 ;; the OS/user uses only one coding-system, then it works.
e29f823e 1046 (coding-system-for-read buffer-file-coding-system))
814838df
SM
1047 (write-region beg1 end1 file1 nil 'nomessage)
1048 (write-region beg2 end2 file2 nil 'nomessage)
3dac25a9
SM
1049 (unwind-protect
1050 (with-current-buffer (get-buffer-create smerge-diff-buffer-name)
d73aed13 1051 (setq default-directory dir)
3dac25a9
SM
1052 (let ((inhibit-read-only t))
1053 (erase-buffer)
814838df
SM
1054 (let ((status
1055 (apply 'call-process diff-command nil t nil
1056 (append smerge-diff-switches
1057 (list "-L" (concat name1 "/" file)
1058 "-L" (concat name2 "/" file)
1059 file1 file2)))))
1060 (if (eq status 0) (insert "No differences found.\n"))))
3dac25a9
SM
1061 (goto-char (point-min))
1062 (diff-mode)
1063 (display-buffer (current-buffer) t))
1064 (delete-file file1)
1065 (delete-file file2))))
1066
814838df
SM
1067;; compiler pacifiers
1068(defvar smerge-ediff-windows)
1069(defvar smerge-ediff-buf)
1070(defvar ediff-buffer-A)
1071(defvar ediff-buffer-B)
1072(defvar ediff-buffer-C)
48d59eda
SM
1073(defvar ediff-ancestor-buffer)
1074(defvar ediff-quit-hook)
004a00f4 1075(declare-function ediff-cleanup-mess "ediff-util" nil)
3dac25a9 1076
a1038ca0 1077;;;###autoload
15092da1
SM
1078(defun smerge-ediff (&optional name-mine name-other name-base)
1079 "Invoke ediff to resolve the conflicts.
1080NAME-MINE, NAME-OTHER, and NAME-BASE, if non-nil, are used for the
1081buffer names."
3dac25a9
SM
1082 (interactive)
1083 (let* ((buf (current-buffer))
1084 (mode major-mode)
1085 ;;(ediff-default-variant 'default-B)
1086 (config (current-window-configuration))
1087 (filename (file-name-nondirectory buffer-file-name))
15092da1
SM
1088 (mine (generate-new-buffer
1089 (or name-mine (concat "*" filename " MINE*"))))
1090 (other (generate-new-buffer
1091 (or name-other (concat "*" filename " OTHER*"))))
3dac25a9
SM
1092 base)
1093 (with-current-buffer mine
1094 (buffer-disable-undo)
1095 (insert-buffer-substring buf)
1096 (goto-char (point-min))
1097 (while (smerge-find-conflict)
1098 (when (match-beginning 2) (setq base t))
5bd8d87b 1099 (smerge-keep-n 1))
3dac25a9
SM
1100 (buffer-enable-undo)
1101 (set-buffer-modified-p nil)
1102 (funcall mode))
1103
1104 (with-current-buffer other
1105 (buffer-disable-undo)
1106 (insert-buffer-substring buf)
1107 (goto-char (point-min))
1108 (while (smerge-find-conflict)
5bd8d87b 1109 (smerge-keep-n 3))
3dac25a9
SM
1110 (buffer-enable-undo)
1111 (set-buffer-modified-p nil)
1112 (funcall mode))
f1180544 1113
3dac25a9 1114 (when base
15092da1
SM
1115 (setq base (generate-new-buffer
1116 (or name-base (concat "*" filename " BASE*"))))
3dac25a9
SM
1117 (with-current-buffer base
1118 (buffer-disable-undo)
1119 (insert-buffer-substring buf)
1120 (goto-char (point-min))
1121 (while (smerge-find-conflict)
5bd8d87b
SM
1122 (if (match-end 2)
1123 (smerge-keep-n 2)
1124 (delete-region (match-beginning 0) (match-end 0))))
3dac25a9
SM
1125 (buffer-enable-undo)
1126 (set-buffer-modified-p nil)
1127 (funcall mode)))
f1180544 1128
3dac25a9
SM
1129 ;; the rest of the code is inspired from vc.el
1130 ;; Fire up ediff.
1131 (set-buffer
1132 (if base
1133 (ediff-merge-buffers-with-ancestor mine other base)
1134 ;; nil 'ediff-merge-revisions-with-ancestor buffer-file-name)
1135 (ediff-merge-buffers mine other)))
1136 ;; nil 'ediff-merge-revisions buffer-file-name)))
f1180544 1137
3dac25a9
SM
1138 ;; Ediff is now set up, and we are in the control buffer.
1139 ;; Do a few further adjustments and take precautions for exit.
1140 (set (make-local-variable 'smerge-ediff-windows) config)
1141 (set (make-local-variable 'smerge-ediff-buf) buf)
1142 (set (make-local-variable 'ediff-quit-hook)
1143 (lambda ()
1144 (let ((buffer-A ediff-buffer-A)
1145 (buffer-B ediff-buffer-B)
1146 (buffer-C ediff-buffer-C)
1147 (buffer-Ancestor ediff-ancestor-buffer)
1148 (buf smerge-ediff-buf)
1149 (windows smerge-ediff-windows))
1150 (ediff-cleanup-mess)
1151 (with-current-buffer buf
1152 (erase-buffer)
a34ed813 1153 (insert-buffer-substring buffer-C)
3dac25a9
SM
1154 (kill-buffer buffer-A)
1155 (kill-buffer buffer-B)
1156 (kill-buffer buffer-C)
1157 (when (bufferp buffer-Ancestor) (kill-buffer buffer-Ancestor))
1158 (set-window-configuration windows)
1159 (message "Conflict resolution finished; you may save the buffer")))))
1160 (message "Please resolve conflicts now; exit ediff when done")))
1161
30e68410
SM
1162(defun smerge-makeup-conflict (pt1 pt2 pt3 &optional pt4)
1163 "Insert diff3 markers to make a new conflict.
1164Uses point and mark for 2 of the relevant positions and previous marks
1165for the other ones.
1166By default, makes up a 2-way conflict,
1167with a \\[universal-argument] prefix, makes up a 3-way conflict."
1168 (interactive
1169 (list (point)
1170 (mark)
1171 (progn (pop-mark) (mark))
1172 (when current-prefix-arg (pop-mark) (mark))))
1173 ;; Start from the end so as to avoid problems with pos-changes.
1174 (destructuring-bind (pt1 pt2 pt3 &optional pt4)
1175 (sort (list* pt1 pt2 pt3 (if pt4 (list pt4))) '>=)
1176 (goto-char pt1) (beginning-of-line)
1177 (insert ">>>>>>> OTHER\n")
1178 (goto-char pt2) (beginning-of-line)
1179 (insert "=======\n")
1180 (goto-char pt3) (beginning-of-line)
1181 (when pt4
1182 (insert "||||||| BASE\n")
1183 (goto-char pt4) (beginning-of-line))
1184 (insert "<<<<<<< MINE\n"))
1185 (if smerge-mode nil (smerge-mode 1))
1186 (smerge-refine))
1187
3dac25a9 1188
de689511
SM
1189(defconst smerge-parsep-re
1190 (concat smerge-begin-re "\\|" smerge-end-re "\\|"
1191 smerge-base-re "\\|" smerge-other-re "\\|"))
1192
3dac25a9
SM
1193;;;###autoload
1194(define-minor-mode smerge-mode
1195 "Minor mode to simplify editing output from the diff3 program.
1196\\{smerge-mode-map}"
c06dbb8f 1197 :group 'smerge :lighter " SMerge"
0304b9c7 1198 (when (and (boundp 'font-lock-mode) font-lock-mode)
3dac25a9
SM
1199 (save-excursion
1200 (if smerge-mode
1201 (font-lock-add-keywords nil smerge-font-lock-keywords 'append)
1202 (font-lock-remove-keywords nil smerge-font-lock-keywords))
1203 (goto-char (point-min))
1204 (while (smerge-find-conflict)
6eabfb26 1205 (save-excursion
48d59eda 1206 (font-lock-fontify-region (match-beginning 0) (match-end 0) nil)))))
de689511
SM
1207 (if (string-match (regexp-quote smerge-parsep-re) paragraph-separate)
1208 (unless smerge-mode
1209 (set (make-local-variable 'paragraph-separate)
1210 (replace-match "" t t paragraph-separate)))
1211 (when smerge-mode
1212 (set (make-local-variable 'paragraph-separate)
1213 (concat smerge-parsep-re paragraph-separate))))
48d59eda
SM
1214 (unless smerge-mode
1215 (smerge-remove-props (point-min) (point-max))))
3dac25a9 1216
ba463d9e 1217;;;###autoload
28e4e2b4 1218(defun smerge-start-session ()
ba463d9e
DN
1219 "Turn on `smerge-mode' and move point to first conflict marker.
1220If no conflict maker is found, turn off `smerge-mode'."
1b7c198a 1221 (interactive)
ba463d9e
DN
1222 (smerge-mode 1)
1223 (condition-case nil
e462b5b8
RS
1224 (unless (looking-at smerge-begin-re)
1225 (smerge-next))
ba463d9e 1226 (error (smerge-auto-leave))))
3dac25a9
SM
1227
1228(provide 'smerge-mode)
ab5796a9 1229
9f0c286d 1230;; arch-tag: 605c8d1e-e43d-4943-a6f3-1bcc4333e690
3dac25a9 1231;;; smerge-mode.el ends here