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