Would this be a good addition to XEmacs? By Jim Hourihan <jimh(a)panix.com>
Jari
;;; cvs-conflict.el --- Minor mode for editing cvs conflicts
;; Copyright (C) 2000 Jim Hourihan
;; Author: Jim Hourihan <jimh(a)panix.com>
;; Created: 2000-11-01
;; Version: 1.3
;; Keywords: tools
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Introduction
;; ------------
;;
;; This package helps you edit files with cvs conflict tags.
;;
;; To use insert in these lines into your ~/.emacs:
;;
;;  (require 'cvs-conflict)
;;  (add-hook 'find-file-hooks 'cvs-conflict-detect)
;;
;; cvs-conflict minor mode will be activated automatically if it
;; detects the presence of cvs conflict tags in the buffer. It will
;; automatically deactivate itself if the buffer is saved and no more
;; tags are detected.
;;
;; cvs-conflict was tested with GNU Emacs 20.5 and greater
;;
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; History:
;;; Code:
(eval-when-compile (require 'cl))
(require 'easymenu)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; User Variables:
;;; Interface to the command system
(defvar cvs-conflict-mode nil
  "Minor mode cvs-conflict indicator.")
(make-variable-buffer-local 'cvs-conflict-mode)
(defvar cvs-conflict-mode-easymenu nil
  "Keymap containing the menu for mode.")
(defgroup cvs-conflict nil
  "Resolve cvs conflicts in a file"
  :group 'tools)
(defcustom cvs-conflict-current-face 'cvs-conflict-current-face
  "*Symbol face used to highlight conflicting text in current file."
  :type 'face
  :group 'cvs-conflict)
(defcustom cvs-conflict-update-face 'cvs-conflict-update-face
  "*Symbol face used to highlight conflicting text from update."
  :type 'face
  :group 'cvs-conflict)
(defcustom cvs-conflict-separater-face 'cvs-conflict-separater-face
  "*Symbol face used to highlight conflicting text from update."
  :type 'face
  :group 'cvs-conflict)
(defface cvs-conflict-current-face '((t (:background "PaleGreen")))
  "Face used to highlight conflicting text in current file.")
(defface cvs-conflict-update-face '((t (:background "Wheat")))
  "Face used to highlight conflicting text from update.")
(defface cvs-conflict-separater-face '((t (:background "Grey")))
  "Face used to highlight conflicting text separaters.")
(defcustom cvs-conflict-verbose t
  "*Verbose messages. Non-nil means generate messages."
  :type 'boolean
  :group 'cvs-conflict)
(defcustom cvs-conflict-invisible-separator nil
  "*Select visible or invisible separator."
  :type 'boolean
  :group 'cvs-conflict)
(defcustom cvs-conflict-invisible-version-names nil
  "*Non-nil means make file name/version invisible."
  :type 'boolean
  :group 'cvs-conflict)
(defcustom cvs-conflict-mode-name-string " Cvs!"  ;; Short name for modeline
  "*The minor mode string displayed when mode is on."
  :type 'string
  :group 'cvs-conflict)
(defcustom cvs-conflict-mode-menu-string "Cvs" ;; Short name for menubar too
  "*The minor mode menu-bar string displayed when mode is on."
  :type 'string
  :group 'cvs-conflict)
(defcustom cvs-conflict-separator-regex "^=======$"
  "*Regexp for separataor of conflict region."
  :type 'regexp
  :group 'cvs-conflict)
(defcustom cvs-conflict-begin-regex "^<<<<<<< "
  "*Regexp for beginning of conflict region."
  :type 'regexp
  :group 'cvs-conflict)
(defcustom cvs-conflict-end-regex "^>>>>>>> "
  "*Regexp for end of conflict region."
  :type 'regexp
  :group 'cvs-conflict)
(defcustom cvs-conflict-load-hook nil
  "*Hook run whan file is loaded."
  :type 'hook
  :group 'cvs-conflict)
(defcustom cvs-conflict-load-hook nil
  "*Hook run when file is loaded."
  :type 'hook
  :group 'cvs-conflict)
(defcustom cvs-conflict-mode-hook nil
  "*Hook run when `cvs-conflict-mode' is called.
Check variable `cvs-conflict-mode'. as needed."
  :type 'hook
  :group 'cvs-conflict)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Customization
;;;###autoload
(defun cvs-conflict-customize ()
  "Customize cvs-conflict group."
  (interactive)
  (customize-group 'cvs-conflict))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; User commands
;;;###autoload
(defun cvs-conflict-mode (&optional arg)
  "Toggle minor mode to aid in cvs source conflict editing.
With ARG, turn cvs-conflict mode on if ARG is positive, off
otherwise.
Installed keys:
\\{cvs-conflict-mode-map}."
  (interactive "P")
  (if (if arg
	  (> (prefix-numeric-value arg) 0)
	(not cvs-conflict-mode))
      (turn-on-cvs-conflict-mode)
    (turn-off-cvs-conflict-mode)))
;;;###autoload
(defun turn-on-cvs-conflict-mode ()
  "Turn on cvs-conflict-mode."
  (interactive)
  (setq cvs-conflict-mode t)
  (cvs-conflict-highlight-buffer)
  (if cvs-conflict-mode-easymenu
      (easy-menu-add cvs-conflict-mode-easymenu))
  (message "cvs-conflict minor mode is on")
  (run-hooks 'cvs-conflict-mode-hook))
;;;###autoload
(defun turn-off-cvs-conflict-mode ()
  "Turn off cvs-conflict-mode."
  (interactive)
  (setq cvs-conflict-mode nil)
  (cvs-conflict-remove-overlays)
  (if cvs-conflict-mode-easymenu
      (easy-menu-remove cvs-conflict-mode-easymenu))
  (message "cvs-conflict minor mode is off")
  (run-hooks 'cvs-conflict-mode-hook))
(defun cvs-conflict-next-conflict ()
  "Find the next conflict region.
Return position on success nil on failure."
  (interactive)
  (end-of-line)
  (if (re-search-forward cvs-conflict-separator-regex nil t)
      (progn (beginning-of-line) (point))
	(beginning-of-line)
	nil))
(defun cvs-conflict-previous-conflict ()
  "Find the previous conflict region.
Return position on success nil on failure."
  (interactive)
  (beginning-of-line)
  (if (re-search-backward cvs-conflict-separator-regex nil t)
      (progn (beginning-of-line) (point))
	(beginning-of-line)
	nil))
(defun cvs-conflict-beginning-of-conflict ()
  "Move to the beginning of conflict region.
Return position on success nil on failure."
  (interactive)
  (while (looking-at "<")
    (forward-char))
  (while (looking-at " ")
    (forward-char))
  (if (re-search-backward cvs-conflict-begin-regex nil t)
      (progn (beginning-of-line) (point))
	(beginning-of-line)
	nil))
(defun cvs-conflict-end-of-conflict ()
  "Move to the end of conflict region.
Return position on success nil on failure"
  (interactive)
  (if (re-search-forward cvs-conflict-end-regex nil t)
      (progn (beginning-of-line) (point))
	(beginning-of-line)
	nil))
(defun cvs-conflict-kill-separators ()
  "Remove merge separators in current conflicts."
  (interactive)
  (save-excursion
    (cvs-conflict-beginning-of-conflict)
    (kill-line 1)
    (while (not (looking-at cvs-conflict-separator-regex)) (forward-line))
    (kill-line 1)
    (while (not (looking-at cvs-conflict-end-regex)) (forward-line))
    (kill-line 1)))
(defun cvs-conflict-reject-other-version ()
  "Remove merge separators in current conflicts.
Reject other version."
  (interactive)
  (save-excursion
    (cvs-conflict-beginning-of-conflict)
    (kill-line 1)
    (while (not (looking-at cvs-conflict-separator-regex)) (forward-line))
    (kill-line 1)
    (while (not (looking-at cvs-conflict-end-regex)) (kill-line 1))
    (kill-line 1)))
(defun cvs-conflict-accept-other-version ()
  "Remove merge separators in current conflicts.
Accept other version."
  (interactive)
  (save-excursion
    (cvs-conflict-beginning-of-conflict)
    (kill-line 1)
    (while (not (looking-at cvs-conflict-separator-regex)) (kill-line 1))
    (kill-line 1)
    (while (not (looking-at cvs-conflict-end-regex)) (forward-line))
    (kill-line 1)))
(defun cvs-conflict-kill ()
  "Remove merge separators and all conflicting text."
  (interactive)
  (save-excursion
    (cvs-conflict-beginning-of-conflict)
    (while (not (looking-at cvs-conflict-end-regex)) (kill-line 1))
    (kill-line 1)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Internal functions
(defun cvs-conflict-in-buffer-p ()
  "Detect if a buffer contains any CVS conflict regions."
  (save-excursion
    (beginning-of-buffer)
    (if (cvs-conflict-next-conflict)
	(if (cvs-conflict-beginning-of-conflict)
	    (if (cvs-conflict-end-of-conflict) t)))))
(defun cvs-conflict-remove-overlays ()
  "Remove all overlays in buffer."
  (interactive)
  (let ((overlays (overlays-in (point-min) (point-max))))
    (while overlays
      (if (memq (overlay-get (first overlays) 'face)
		'(cvs-conflict-separater-face
		  cvs-conflict-current-face
		  cvs-conflict-update-face))
	  (delete-overlay (first overlays)))
      (setq overlays (rest overlays)))))
(defun cvs-conflict-highlight-line (highlight-face &optional invis)
  "Highlight line with given HIGHLIGHT-FACE optionally INVIS."
  (let ((overlay (make-overlay (line-beginning-position)
			       (1+ (line-end-position)))))
    (overlay-put overlay 'hilit t)
    (overlay-put overlay 'invisible invis)
    (overlay-put overlay 'face highlight-face)
    (overlay-put overlay 'priority 0)))
(defun cvs-conflict-highlight-buffer ()
  "Highlight conflicts."
  (interactive)
  (save-excursion
    (cvs-conflict-remove-overlays)
    (let ((count 0))
      (beginning-of-buffer)
      (while (cvs-conflict-next-conflict)
	(setq count (1+ count))
	;; highlight the separator
	(cvs-conflict-highlight-line 'cvs-conflict-separater-face
				     cvs-conflict-invisible-separator)
	(if (cvs-conflict-end-of-conflict)
	    (cvs-conflict-highlight-line 'cvs-conflict-separater-face
					 cvs-conflict-invisible-version-names))
	(if (cvs-conflict-beginning-of-conflict)
	    (cvs-conflict-highlight-line 'cvs-conflict-separater-face
					 cvs-conflict-invisible-version-names))
	(forward-line)
	(while (not (looking-at cvs-conflict-separator-regex))
	  (cvs-conflict-highlight-line 'cvs-conflict-current-face)
	  (forward-line))
	(forward-line)
	(while (not (looking-at cvs-conflict-end-regex))
	  (cvs-conflict-highlight-line 'cvs-conflict-update-face)
	  (forward-line)))
      ;(message "%d conflicts" count)
      )))
(defun cvs-conflict-detect ()
  (if (cvs-conflict-in-buffer-p)
      (turn-on-cvs-conflict-mode)
    (if (eq cvs-conflict-mode t)
	(turn-off-cvs-conflict-mode))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun cvs-conflict-mode-map ()
  "Return keymap for `cvs-conflict-mode'."
  (let ((map (make-sparse-keymap)))
    (define-key map "\C-cn" 'cvs-conflict-next-conflict)
    (define-key map "\C-cp" 'cvs-conflict-previous-conflict)
    (define-key map "\C-ca" 'cvs-conflict-beginning-of-conflict)
    (define-key map "\C-ce" 'cvs-conflict-end-of-conflict)
    (define-key map "\C-cr" 'cvs-conflict-reject-other-version)
    (define-key map "\C-co" 'cvs-conflict-accept-other-version)
    (define-key map "\C-cl" 'cvs-conflict-highlight-buffer)
    (define-key map "\C-ck" 'cvs-conflict-kill-separators)
    (define-key map "\C-c\d" 'cvs-conflict-kill)
    ;; And some Window-system fast keys.
    (define-key map [(control backsapce)] 'cvs-conflict-kill)
    (define-key map [(control delete)] 'cvs-conflict-kill)
    map))
(defvar cvs-conflict-mode-map (cvs-conflict-mode-map)
  "*Keymap of `cvs-conflict-mode'.
Installed keys:
\\{cvs-conflict-mode-map}.")
(defun cvs-conflict-mode-menu ()
  "Return menu for `cvs-conflict-mode'."
  (list
   cvs-conflict-mode-menu-string
   ["Next"		    cvs-conflict-next-conflict		t]
   ["Previous"		    cvs-conflict-previous-conflict	t]
   ["Beginning"		    cvs-conflict-beginning-of-conflict	t]
   ["End"		    cvs-conflict-end-of-conflict	t]
   "----"
   ["Reject Other Version"  cvs-conflict-reject-other-version	t]
   ["Use Other Version"	    cvs-conflict-accept-other-version	t]
   ["Kill Separators"	    cvs-conflict-kill-separators	t]
   ["Kill Conflict"	    cvs-conflict-kill			t]
   "----"
   ["Update"		    cvs-conflict-highlight-buffer	t]
   ["Turn Off"		    turn-off-cvs-conflict-mode		t]
   ))
(defun cvs-conflict-mode-define ()
  "Define `cvs-conflict-mode'."
   (if (or (boundp 'xemacs-logo)
	   (featurep 'xemacs))
       ;; Don't know why, but easy-menu-do-define works in XEmacs,
       ;; but easy-menu-define deos not.
       (easy-menu-do-define 'cvs-conflict-mode-easymenu
			    nil
			    "Cvs Conflict"
			    (cvs-conflict-mode-menu))
     (easy-menu-define 'cvs-conflict-mode-easymenu
		       cvs-conflict-mode-map
		       "Cvs Conflict"
		       (cvs-conflict-mode-menu))))
(defun cvs-conflict-mode-install (&optional uninstall)
  "Install or UNINSTALL minor mode `cvs-conflict-mode'."
  ;; Uninstall is automatic / i.e re-install
  (setq minor-mode-map-alist
	(delete (assq 'cvs-conflict-mode minor-mode-map-alist)
		minor-mode-map-alist))
  (setq minor-mode-alist
	(delete (assq 'cvs-conflict-mode minor-mode-alist)
		minor-mode-alist))
  (unless uninstall
    (cvs-conflict-mode-define)
    ;;  Do not install multiple copies, use pushnew with :test
    (pushnew '(cvs-conflict-mode cvs-conflict-mode-name-string)
	     minor-mode-alist
	     :test 'equal)
    (pushnew (cons 'cvs-conflict-mode cvs-conflict-mode-map)
	     minor-mode-map-alist
	     :test 'equal)))
(cvs-conflict-mode-install)
(provide 'cvs-conflict)
(run-hooks 'cvs-conflict-load-hook)
;;; cvs-conflict.el ends here