(toplevel): Provide `descr-text'.
[bpt/emacs.git] / lisp / smerge-mode.el
index 14b0a3a..5ce9328 100644 (file)
@@ -1,10 +1,10 @@
 ;;; 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$
+;; Revision: $Id: smerge-mode.el,v 1.14 2001/07/31 08:28:43 gerd Exp $
 
 ;; This file is part of GNU Emacs.
 
 ;;        (smerge-mode 1))))
 ;;   (add-hook 'find-file-hooks 'sm-try-smerge t)
 
+;;; Todo:
+
+;; - if requested, ask the user whether he wants to call ediff right away
+
 ;;; Code:
 
 (eval-when-compile (require 'cl))
          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 '(repeat string))
 
+(defcustom smerge-auto-leave t
+  "*Non-nil means to leave `smerge-mode' when the last conflict is resolved."
+  :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)
 
-(defmacro smerge-defmap (var bindings &optional doc)
-  `(defvar ,var
-     (let ((m (make-sparse-keymap)))
-       (dolist (b ,bindings)
-        (if (equal (car b) "")
-            (set-keymap-parent m (cdr b))
-          (define-key m (car b) (cdr b))))
-       m)
-     ,doc))
-
-(smerge-defmap smerge-basic-keymap
-  '(("n" . smerge-next)
+(easy-mmode-defmap smerge-basic-map
+  `(("n" . smerge-next)
     ("p" . smerge-prev)
     ("a" . smerge-keep-all)
     ("b" . smerge-keep-base)
@@ -110,32 +121,49 @@ Used in `smerge-diff-base-mine' and related functions."
     ("m" . smerge-keep-mine)
     ("E" . smerge-ediff)
     ("\C-m" . smerge-keep-current)
-    ("<" . smerge-diff-base-mine)
-    (">" . smerge-diff-base-other)
-    ("=" . 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'.")
-(fset 'smerge-basic-keymap smerge-basic-keymap)
 
-(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))
 
-(smerge-defmap smerge-mode-map
-  `((,smerge-command-prefix . smerge-basic-keymap))
+(easy-mmode-defmap smerge-mode-map
+  `((,smerge-command-prefix . ,smerge-basic-map))
   "Keymap for `smerge-mode'.")
 
 (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)))
@@ -153,8 +181,8 @@ Can be nil if the style is undecided, or else:
 - `diff3-A'")
 
 ;; Compiler pacifiers
-(defvar font-lock-mode nil)
-(defvar font-lock-keywords nil)
+(defvar font-lock-mode)
+(defvar font-lock-keywords)
 (eval-when-compile
   (unless (fboundp 'font-lock-fontify-region)
     (autoload 'font-lock-fontify-region "font-lock")))
@@ -163,23 +191,8 @@ Can be nil if the style is undecided, or else:
 ;;;; Actual code
 ;;;;
 
-(defun smerge-next (&optional count)
-  "Go to the next COUNT'th conflict."
-  (interactive)
-  (unless count (setq count 1))
-  (if (< count 0) (smerge-prev (- count))
-    (if (looking-at smerge-begin-re) (incf count))
-    (unless (re-search-forward smerge-begin-re nil t count)
-      (error "No next conflict"))
-    (goto-char (match-beginning 0))))
-
-(defun smerge-prev (&optional count)
-  "Go to the previous COUNT'th conflict."
-  (interactive)
-  (unless count (setq count 1))
-  (if (< count 0) (smerge-next (- count))
-    (unless (re-search-backward smerge-begin-re nil t count)
-      (error "No previous conflict"))))
+;; Define smerge-next and smerge-prev
+(easy-mmode-define-navigation smerge smerge-begin-re "conflict")
 
 (defconst smerge-match-names ["conflict" "mine" "base" "other"])
 
@@ -187,6 +200,13 @@ Can be nil if the style is undecided, or else:
   (unless (match-end n)
     (error (format "No `%s'" (aref smerge-match-names n)))))
 
+(defun smerge-auto-leave ()
+  (when (and smerge-auto-leave
+            (save-excursion (goto-char (point-min))
+                            (not (re-search-forward smerge-begin-re nil t))))
+    (smerge-mode -1)))
+    
+
 (defun smerge-keep-all ()
   "Keep all three versions.
 Convenient for the kind of conflicts that can arise in ChangeLog files."
@@ -195,28 +215,32 @@ Convenient for the kind of conflicts that can arise in ChangeLog files."
   (replace-match (concat (or (match-string 1) "")
                         (or (match-string 2) "")
                         (or (match-string 3) ""))
-                t t))
+                t t)
+  (smerge-auto-leave))
 
 (defun smerge-keep-base ()
   "Revert to the base version."
   (interactive)
   (smerge-match-conflict)
   (smerge-ensure-match 2)
-  (replace-match (match-string 2) t t))
+  (replace-match (match-string 2) t t)
+  (smerge-auto-leave))
 
 (defun smerge-keep-other ()
   "Use \"other\" version."
   (interactive)
   (smerge-match-conflict)
   ;;(smerge-ensure-match 3)
-  (replace-match (match-string 3) t t))
+  (replace-match (match-string 3) t t)
+  (smerge-auto-leave))
 
 (defun smerge-keep-mine ()
   "Keep your version."
   (interactive)
   (smerge-match-conflict)
   ;;(smerge-ensure-match 1)
-  (replace-match (match-string 1) t t))
+  (replace-match (match-string 1) t t)
+  (smerge-auto-leave))
 
 (defun smerge-keep-current ()
   "Use the current (under the cursor) version."
@@ -228,7 +252,8 @@ Convenient for the kind of conflicts that can arise in ChangeLog files."
               (>= (point) (match-end i)))
       (decf i))
     (if (<= i 0) (error "Not inside a version")
-      (replace-match (match-string i) t t))))
+      (replace-match (match-string i) t t)
+      (smerge-auto-leave))))
 
 (defun smerge-diff-base-mine ()
   "Diff 'base' and 'mine' version in current conflict region."
@@ -307,7 +332,7 @@ An error is raised if not inside a conflict."
                                  (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.
@@ -325,17 +350,28 @@ The point is moved to the end of the conflict."
   (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)
+    (write-region beg2 end2 file2)
     (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))))
+                          (list "-L" (concat name1 "/" file)
+                                "-L" (concat name2 "/" file)
+                                file1 file2))))
          (goto-char (point-min))
          (diff-mode)
          (display-buffer (current-buffer) t))
@@ -435,19 +471,17 @@ The point is moved to the end of the conflict."
   "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 ends here