;;; smerge-mode.el --- Minor mode to resolve diff3 conflicts
-;; Copyright (C) 1999 Free Software Foundation, Inc.
+;; Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
;; Author: Stefan Monnier <monnier@cs.yale.edu>
;; Keywords: merge diff3 cvs conflict
-;; Revision: $Id: smerge-mode.el,v 1.1 1999/12/09 13:00:21 monnier Exp $
+;; Revision: $Id: smerge-mode.el,v 1.19 2002/10/10 13:01:14 monnier Exp $
;; This file is part of GNU Emacs.
;; (goto-char (point-min))
;; (when (re-search-forward "^<<<<<<< " nil t)
;; (smerge-mode 1))))
-;; (add-hook 'find-file-hooks 'sm-try-smerge t)
+;; (add-hook 'find-file-hook 'sm-try-smerge t)
;;; Todo:
:group 'tools
:prefix "smerge-")
-(defcustom smerge-diff-buffer-name "*smerge-diff*"
+(defcustom smerge-diff-buffer-name "*vc-diff*"
"Buffer name to use for displaying diffs."
:group 'smerge
:type '(choice
string))
(defcustom smerge-diff-switches
- (list* "-d" "-b"
- (if (listp diff-switches) diff-switches (list diff-switches)))
+ (append '("-d" "-b")
+ (if (listp diff-switches) diff-switches (list diff-switches)))
"*A list of strings specifying switches to be be passed to diff.
Used in `smerge-diff-base-mine' and related functions."
:group 'smerge
:type 'boolean)
(defface smerge-mine-face
- '((t (:foreground "blue")))
+ '((((background light))
+ (:foreground "blue"))
+ (((background dark))
+ (:foreground "cyan")))
"Face for your code."
:group 'smerge)
(defvar smerge-mine-face 'smerge-mine-face)
(defface smerge-other-face
- '((t (:foreground "darkgreen")))
+ '((((background light))
+ (:foreground "darkgreen"))
+ (((background dark))
+ (:foreground "lightgreen")))
"Face for the other code."
:group 'smerge)
(defvar smerge-other-face 'smerge-other-face)
(defface smerge-base-face
- '((t (:foreground "red")))
+ '((((background light))
+ (:foreground "red"))
+ (((background dark))
+ (:foreground "orange")))
"Face for the base code."
:group 'smerge)
(defvar smerge-base-face 'smerge-base-face)
(defface smerge-markers-face
- '((t (:background "grey85")))
+ '((((background light))
+ (:background "grey85"))
+ (((background dark))
+ (:background "grey30")))
"Face for the conflict markers."
:group 'smerge)
(defvar smerge-markers-face 'smerge-markers-face)
(easy-mmode-defmap smerge-basic-map
- '(("n" . smerge-next)
+ `(("n" . smerge-next)
("p" . smerge-prev)
+ ("r" . smerge-resolve)
("a" . smerge-keep-all)
("b" . smerge-keep-base)
("o" . smerge-keep-other)
("m" . smerge-keep-mine)
("E" . smerge-ediff)
("\C-m" . smerge-keep-current)
- ("d<" . smerge-diff-base-mine)
- ("d>" . smerge-diff-base-other)
- ("d=" . smerge-diff-mine-other))
+ ("=" . ,(make-sparse-keymap "Diff"))
+ ("=<" "base-mine" . smerge-diff-base-mine)
+ ("=>" "base-other" . smerge-diff-base-other)
+ ("==" "mine-other" . smerge-diff-mine-other))
"The base keymap for `smerge-mode'.")
-(defcustom smerge-command-prefix "\e"
+(defcustom smerge-command-prefix "\C-c^"
"Prefix for `smerge-mode' commands."
:group 'smerge
- :type '(choice (string "\e") (string "C-x^") (string "") string))
+ :type '(choice (string "\e") (string "\C-c^") (string "") string))
(easy-mmode-defmap smerge-mode-map
`((,smerge-command-prefix . ,smerge-basic-map))
(easy-menu-define smerge-mode-menu smerge-mode-map
"Menu for `smerge-mode'."
'("SMerge"
- ["Invoke Ediff" smerge-ediff t]
+ ["Next" smerge-next :help "Go to next conflict"]
+ ["Previous" smerge-prev :help "Go to previous conflict"]
+ ["Keep All" smerge-keep-all :help "Keep all three versions"]
+ ["Revert to Base" smerge-keep-base :help "Revert to base version"]
+ ["Keep Other" smerge-keep-other :help "Keep `other' version"]
+ ["Keep Yours" smerge-keep-mine :help "Keep your version"]
+ ["Keep Current" smerge-keep-current :help "Use current (at point) version"]
+ "--"
+ ["Diff Base/Mine" smerge-diff-base-mine
+ :help "Diff `base' and `mine' for current conflict"]
+ ["Diff Base/Other" smerge-diff-base-other
+ :help "Diff `base' and `other' for current conflict"]
+ ["Diff Mine/Other" smerge-diff-mine-other
+ :help "Diff `mine' and `other' for current conflict"]
+ "--"
+ ["Invoke Ediff" smerge-ediff
+ :help "Use Ediff to resolve the conflicts"]
))
(defconst smerge-font-lock-keywords
'((smerge-find-conflict
- (1 smerge-mine-face prepend)
+ (1 smerge-mine-face prepend t)
(2 smerge-base-face prepend t)
(3 smerge-other-face prepend t)
+ ;; FIXME: `keep' doesn't work right with syntactic fontification.
(0 smerge-markers-face keep)
(4 nil t t)
(5 nil t t)))
- `diff3-A'")
;; Compiler pacifiers
-(defvar font-lock-mode nil)
-(defvar font-lock-keywords nil)
-(eval-when-compile
- (unless (fboundp 'font-lock-fontify-region)
- (autoload 'font-lock-fontify-region "font-lock")))
+(defvar font-lock-mode)
+(defvar font-lock-keywords)
;;;;
;;;; Actual code
t t)
(smerge-auto-leave))
+(defun smerge-combine-with-next ()
+ "Combine the current conflict with the next one."
+ (interactive)
+ (smerge-match-conflict)
+ (let ((ends nil))
+ (dolist (i '(3 2 1 0))
+ (push (if (match-end i) (copy-marker (match-end i) t)) ends))
+ (setq ends (apply 'vector ends))
+ (goto-char (aref ends 0))
+ (if (not (re-search-forward smerge-begin-re nil t))
+ (error "No next conflict")
+ (smerge-match-conflict)
+ (let ((match-data (mapcar (lambda (m) (if m (copy-marker m)))
+ (match-data))))
+ ;; First copy the in-between text in each alternative.
+ (dolist (i '(1 2 3))
+ (when (aref ends i)
+ (goto-char (aref ends i))
+ (insert-buffer-substring (current-buffer)
+ (aref ends 0) (car match-data))))
+ (delete-region (aref ends 0) (car match-data))
+ ;; Then move the second conflict's alternatives into the first.
+ (dolist (i '(1 2 3))
+ (set-match-data match-data)
+ (when (and (aref ends i) (match-end i))
+ (goto-char (aref ends i))
+ (insert-buffer-substring (current-buffer)
+ (match-beginning i) (match-end i))))
+ (delete-region (car match-data) (cadr match-data))
+ ;; Free the markers.
+ (dolist (m match-data) (if m (move-marker m nil)))
+ (mapc (lambda (m) (if m (move-marker m nil))) ends)))))
+
+(defvar smerge-resolve-function
+ (lambda () (error "Don't know how to resolve"))
+ "Mode-specific merge function.
+The function is called with no argument and with the match data set
+according to `smerge-match-conflict'.")
+
+(defun smerge-resolve ()
+ "Resolve the conflict at point intelligently.
+This relies on mode-specific knowledge and thus only works in
+some major modes. Uses `smerge-resolve-function' to do the actual work."
+ (interactive)
+ (smerge-match-conflict)
+ (funcall smerge-resolve-function)
+ (smerge-auto-leave))
+
(defun smerge-keep-base ()
"Revert to the base version."
(interactive)
(start (match-beginning 0))
(mine-start (match-end 0))
- (filename (match-string 1))
+ (filename (or (match-string 1) ""))
(_ (re-search-forward smerge-end-re))
(_ (assert (< orig-point (match-end 0))))
(when base-start (1- base-start)) base-start
(1- other-start) other-start))
t)
- (error "Point not in conflict region"))))
+ (search-failed (error "Point not in conflict region")))))
(defun smerge-find-conflict (&optional limit)
"Find and match a conflict region. Intended as a font-lock MATCHER.
(smerge-ensure-match n2)
(let ((name1 (aref smerge-match-names n1))
(name2 (aref smerge-match-names n2))
+ ;; Read them before the match-data gets clobbered.
+ (beg1 (match-beginning n1))
+ (end1 (match-end n1))
+ (beg2 (match-beginning n2))
+ (end2 (match-end n2))
(file1 (make-temp-file "smerge1"))
- (file2 (make-temp-file "smerge2")))
- (write-region (match-beginning n1) (match-end n1) file1)
- (write-region (match-beginning n2) (match-end n2) file2)
+ (file2 (make-temp-file "smerge2"))
+ (dir default-directory)
+ (file (file-relative-name buffer-file-name))
+ (coding-system-for-read buffer-file-coding-system))
+ (write-region beg1 end1 file1 nil 'nomessage)
+ (write-region beg2 end2 file2 nil 'nomessage)
(unwind-protect
(with-current-buffer (get-buffer-create smerge-diff-buffer-name)
+ (setq default-directory dir)
(let ((inhibit-read-only t))
(erase-buffer)
- (apply 'call-process diff-command nil t nil
- (append smerge-diff-switches
- (list "-L" name1 "-L" name2 file1 file2))))
+ (let ((status
+ (apply 'call-process diff-command nil t nil
+ (append smerge-diff-switches
+ (list "-L" (concat name1 "/" file)
+ "-L" (concat name2 "/" file)
+ file1 file2)))))
+ (if (eq status 0) (insert "No differences found.\n"))))
(goto-char (point-min))
(diff-mode)
(display-buffer (current-buffer) t))
(delete-file file1)
(delete-file file2))))
-(eval-when-compile
- ;; compiler pacifiers
- (defvar smerge-ediff-windows)
- (defvar smerge-ediff-buf)
- (defvar ediff-buffer-A)
- (defvar ediff-buffer-B)
- (defvar ediff-buffer-C)
- (unless (fboundp 'ediff-cleanup-mess)
- (autoload 'ediff-cleanup-mess "ediff-util")))
-
-(defun smerge-ediff ()
- "Invoke ediff to resolve the conflicts."
+;; compiler pacifiers
+(defvar smerge-ediff-windows)
+(defvar smerge-ediff-buf)
+(defvar ediff-buffer-A)
+(defvar ediff-buffer-B)
+(defvar ediff-buffer-C)
+
+;;;###autoload
+(defun smerge-ediff (&optional name-mine name-other name-base)
+ "Invoke ediff to resolve the conflicts.
+NAME-MINE, NAME-OTHER, and NAME-BASE, if non-nil, are used for the
+buffer names."
(interactive)
(let* ((buf (current-buffer))
(mode major-mode)
;;(ediff-default-variant 'default-B)
(config (current-window-configuration))
(filename (file-name-nondirectory buffer-file-name))
- (mine (generate-new-buffer (concat "*" filename " MINE*")))
- (other (generate-new-buffer (concat "*" filename " OTHER*")))
+ (mine (generate-new-buffer
+ (or name-mine (concat "*" filename " MINE*"))))
+ (other (generate-new-buffer
+ (or name-other (concat "*" filename " OTHER*"))))
base)
(with-current-buffer mine
(buffer-disable-undo)
(funcall mode))
(when base
- (setq base (generate-new-buffer (concat "*" filename " BASE*")))
+ (setq base (generate-new-buffer
+ (or name-base (concat "*" filename " BASE*"))))
(with-current-buffer base
(buffer-disable-undo)
(insert-buffer-substring buf)
"Minor mode to simplify editing output from the diff3 program.
\\{smerge-mode-map}"
nil " SMerge" nil
- (when font-lock-mode
+ (when (and (boundp 'font-lock-mode) font-lock-mode)
+ (set (make-local-variable 'font-lock-multiline) t)
(save-excursion
(if smerge-mode
(font-lock-add-keywords nil smerge-font-lock-keywords 'append)
(font-lock-remove-keywords nil smerge-font-lock-keywords))
(goto-char (point-min))
(while (smerge-find-conflict)
- (font-lock-fontify-region (match-beginning 0) (match-end 0) nil)))))
+ (save-excursion
+ (font-lock-fontify-region (match-beginning 0) (match-end 0) nil))))))
(provide 'smerge-mode)
-
-;;; Change Log:
-;; $Log: smerge-mode.el,v $
-;; Revision 1.1 1999/12/09 13:00:21 monnier
-;; New file. Provides a simple minor-mode for files containing
-;; diff3-style conflict markers, such as generated by RCS
-;;
-
;;; smerge-mode.el ends here