(struct window): New member height_fixed_p.
[bpt/emacs.git] / lisp / emerge.el
index d84812a..023727e 100644 (file)
@@ -2,7 +2,7 @@
 
 ;;; The author has placed this file in the public domain.
 
-;; Author: Dale R. Worley <drw@math.mit.edu>
+;; Author: Dale R. Worley <worley@world.std.com>
 ;; Version: 5fsf
 ;; Keywords: unix, tools
 
 
 ;;; Code:
 
+;;;###autoload
+(defvar menu-bar-emerge-menu (make-sparse-keymap "Emerge"))
+;;;###autoload
+(fset 'menu-bar-emerge-menu (symbol-value 'menu-bar-emerge-menu))
+
+;;;###autoload
+(define-key menu-bar-emerge-menu [emerge-merge-directories]
+  '("Merge Directories..." . emerge-merge-directories))
+;;;###autoload
+(define-key menu-bar-emerge-menu [emerge-revisions-with-ancestor]
+  '("Revisions with Ancestor..." . emerge-revisions-with-ancestor))
+;;;###autoload
+(define-key menu-bar-emerge-menu [emerge-revisions]
+  '("Revisions..." . emerge-revisions))
+;;;###autoload
+(define-key menu-bar-emerge-menu [emerge-files-with-ancestor]
+  '("Files with Ancestor..." . emerge-files-with-ancestor))
+;;;###autoload
+(define-key menu-bar-emerge-menu [emerge-files]
+  '("Files..." . emerge-files))
+;;;###autoload
+(define-key menu-bar-emerge-menu [emerge-buffers-with-ancestor]
+  '("Buffers with Ancestor..." . emerge-buffers-with-ancestor))
+;;;###autoload
+(define-key menu-bar-emerge-menu [emerge-buffers]
+  '("Buffers..." . emerge-buffers))
+
 ;;; Macros
 
 (defmacro emerge-eval-in-buffer (buffer &rest forms)
@@ -45,11 +72,12 @@ won't destroy Emerge control variables."
        (put '(, var) 'preserved t))))
 
 ;; Add entries to minor-mode-alist so that emerge modes show correctly
-(setq emerge-minor-modes-list '((emerge-mode " Emerge")
-                               (emerge-fast-mode " F")
-                               (emerge-edit-mode " E")
-                               (emerge-auto-advance " A")
-                               (emerge-skip-prefers " S")))
+(defvar emerge-minor-modes-list
+  '((emerge-mode " Emerge")
+    (emerge-fast-mode " F")
+    (emerge-edit-mode " E")
+    (emerge-auto-advance " A")
+    (emerge-skip-prefers " S")))
 (if (not (assq 'emerge-mode minor-mode-alist))
     (setq minor-mode-alist (append emerge-minor-modes-list
                                   minor-mode-alist)))
@@ -85,6 +113,10 @@ When called interactively, displays the version."
 
 ;;; Emerge configuration variables
 
+(defgroup emerge nil
+  "Merge diffs under Emacs control."
+  :group 'tools)
+
 ;; Commands that produce difference files
 ;; All that can be configured is the name of the programs to execute
 ;; (emerge-diff-program and emerge-diff3-program) and the options
@@ -96,60 +128,102 @@ When called interactively, displays the version."
 ;; The code which processes the diff/diff3 output depends on all the
 ;; finicky details of their output, including the somewhat strange
 ;; way they number lines of a file.
-(defvar emerge-diff-program "diff"
-  "*Name of the program which compares two files.")
-(defvar emerge-diff3-program "diff3"
+(defcustom emerge-diff-program "diff"
+  "*Name of the program which compares two files."
+  :type 'string
+  :group 'emerge)
+(defcustom emerge-diff3-program "diff3"
   "*Name of the program which compares three files.
-Its arguments are the ancestor file and the two variant files.")
-(defvar emerge-diff-options ""
-  "*Options to pass to `emerge-diff-program' and `emerge-diff3-program'.")
-(defvar emerge-match-diff-line (let ((x "\\([0-9]+\\)\\(\\|,\\([0-9]+\\)\\)"))
-                                (concat "^" x "\\([acd]\\)" x "$"))
+Its arguments are the ancestor file and the two variant files."
+  :type 'string
+  :group 'emerge)
+(defcustom emerge-diff-options ""
+  "*Options to pass to `emerge-diff-program' and `emerge-diff3-program'."
+  :type 'string
+  :group 'emerge)
+(defcustom emerge-match-diff-line
+  (let ((x "\\([0-9]+\\)\\(\\|,\\([0-9]+\\)\\)"))
+    (concat "^" x "\\([acd]\\)" x "$"))
   "*Pattern to match lines produced by diff that describe differences.
-This is as opposed to lines from the source files.")
-(defvar emerge-diff-ok-lines-regexp
+This is as opposed to lines from the source files."
+  :type 'regexp
+  :group 'emerge)
+(defcustom emerge-diff-ok-lines-regexp
   "^\\([0-9,]+[acd][0-9,]+$\\|[<>] \\|---\\)"
   "*Regexp that matches normal output lines from `emerge-diff-program'.
-Lines that do not match are assumed to be error messages.")
-(defvar emerge-diff3-ok-lines-regexp
+Lines that do not match are assumed to be error messages."
+  :type 'regexp
+  :group 'emerge)
+(defcustom emerge-diff3-ok-lines-regexp
   "^\\([1-3]:\\|====\\|  \\)"
   "*Regexp that matches normal output lines from `emerge-diff3-program'.
-Lines that do not match are assumed to be error messages.")
-
-(defvar emerge-rcs-ci-program "ci"
-  "*Name of the program that checks in RCS revisions.")
-(defvar emerge-rcs-co-program "co"
-  "*Name of the program that checks out RCS revisions.")
-
-(defvar emerge-process-local-variables nil
+Lines that do not match are assumed to be error messages."
+  :type 'regexp
+  :group 'emerge)
+
+(defcustom emerge-rcs-ci-program "ci"
+  "*Name of the program that checks in RCS revisions."
+  :type 'string
+  :group 'emerge)
+(defcustom emerge-rcs-co-program "co"
+  "*Name of the program that checks out RCS revisions."
+  :type 'string
+  :group 'emerge)
+
+(defcustom emerge-process-local-variables nil
   "*Non-nil if Emerge should process local-variables lists in merge buffers.
 \(You can explicitly request processing the local-variables
-by executing `(hack-local-variables)'.)")
-(defvar emerge-execute-line-deletions nil
+by executing `(hack-local-variables)'.)"
+  :type 'boolean
+  :group 'emerge)
+(defcustom emerge-execute-line-deletions nil
   "*If non-nil: `emerge-execute-line' makes no output if an input was deleted.
 It concludes that an input version has been deleted when an ancestor entry
 is present, only one A or B entry is present, and an output entry is present.
 If nil: In such circumstances, the A or B file that is present will be
-copied to the designated output file.")
+copied to the designated output file."
+  :type 'boolean
+  :group 'emerge)
+
+(defcustom emerge-before-flag "vvvvvvvvvvvvvvvvvvvv\n"
+  "*Flag placed above the highlighted block of code.  Must end with newline.
+Must be set before Emerge is loaded, or  emerge-new-flags  must be run
+after setting."
+  :type 'string
+  :group 'emerge)
+(defcustom emerge-after-flag "^^^^^^^^^^^^^^^^^^^^\n"
+  "*Flag placed below the highlighted block of code.  Must end with newline.
+Must be set before Emerge is loaded, or  emerge-new-flags  must be run
+after setting."
+  :type 'string
+  :group 'emerge)
 
 ;; Hook variables
 
-(defvar emerge-startup-hook nil
-  "*Hook to run in the merge buffer after the merge has been set up.")
-(defvar emerge-select-hook nil
+(defcustom emerge-startup-hook nil
+  "*Hook to run in the merge buffer after the merge has been set up."
+  :type 'hook
+  :group 'emerge)
+(defcustom emerge-select-hook nil
   "*Hook to run after a difference has been selected.
-The variable `n' holds the (internal) number of the difference.")
-(defvar emerge-unselect-hook nil
+The variable `n' holds the (internal) number of the difference."
+  :type 'hook
+  :group 'emerge)
+(defcustom emerge-unselect-hook nil
   "*Hook to run after a difference has been unselected.
-The variable `n' holds the (internal) number of the difference.")
+The variable `n' holds the (internal) number of the difference."
+  :type 'hook
+  :group 'emerge)
 
 ;; Variables to control the default directories of the arguments to
 ;; Emerge commands.
 
-(defvar emerge-default-last-directories nil
+(defcustom emerge-default-last-directories nil
   "*If nil, default dir for filenames in emerge is `default-directory'.
 If non-nil, filenames complete in the directory of the last argument of the
-same type to an `emerge-files...' command.")
+same type to an `emerge-files...' command."
+  :type 'boolean
+  :group 'emerge)
 
 (defvar emerge-last-dir-A nil
   "Last directory for the first file of an `emerge-files...' command.")
@@ -166,6 +240,20 @@ same type to an `emerge-files...' command.")
 (defvar emerge-last-revision-ancestor nil
   "Last RCS revision used for ancestor file of an `emerge-revisions...' command.")
 
+(defvar emerge-before-flag-length)
+(defvar emerge-before-flag-lines)
+(defvar emerge-before-flag-match)
+(defvar emerge-after-flag-length)
+(defvar emerge-after-flag-lines)
+(defvar emerge-after-flag-match)
+(defvar emerge-diff-buffer)
+(defvar emerge-diff-error-buffer)
+(defvar emerge-prefix-argument)
+(defvar emerge-file-out)
+(defvar emerge-exit-func)
+(defvar emerge-globalized-difference-list)
+(defvar emerge-globalized-number-of-differences)
+
 ;; The flags used to mark differences in the buffers.
 
 ;; These function definitions need to be up here, because they are used
@@ -192,39 +280,29 @@ depend on the flags."
       (setq i (match-end 0)))
     count))
 
-(defvar emerge-before-flag "vvvvvvvvvvvvvvvvvvvv\n"
-  "*Flag placed above the highlighted block of code.  Must end with newline.
-Must be set before Emerge is loaded, or  emerge-new-flags  must be run
-after setting.")
-(defvar emerge-after-flag "^^^^^^^^^^^^^^^^^^^^\n"
-  "*Flag placed below the highlighted block of code.  Must end with newline.
-Must be set before Emerge is loaded, or  emerge-new-flags  must be run
-after setting.")
-
 ;; Calculate dependent variables
 (emerge-new-flags)
 
-(defvar emerge-min-visible-lines 3
+(defcustom emerge-min-visible-lines 3
   "*Number of lines that we want to show above and below the flags when we are
-displaying a difference.")
-
-(defvar emerge-temp-file-prefix
-  (let ((env (getenv "TMPDIR"))
-       d)
-    (setq d (if (and env (> (length env) 0))
-               env
-             "/tmp"))
-    (if (= (aref d (1- (length d))) ?/)
-       (setq d (substring d 0 -1)))
-    (concat d "/emerge"))
+displaying a difference."
+  :type 'integer
+  :group 'emerge)
+
+(defcustom emerge-temp-file-prefix
+  (expand-file-name "emerge" temporary-file-directory)
   "*Prefix to put on Emerge temporary file names.
-Do not start with `~/' or `~user-name/'.")
+Do not start with `~/' or `~USERNAME/'."
+  :type 'string
+  :group 'emerge)
 
-(defvar emerge-temp-file-mode 384      ; u=rw only
-  "*Mode for Emerge temporary files.")
+(defcustom emerge-temp-file-mode 384   ; u=rw only
+  "*Mode for Emerge temporary files."
+  :type 'integer
+  :group 'emerge)
 
-(defvar emerge-combine-versions-template
-  "#ifdef NEW\n%b#else /* NEW */\n%a#endif /* NEW */\n"
+(defcustom emerge-combine-versions-template
+  "#ifdef NEW\n%b#else /* not NEW */\n%a#endif /* not NEW */\n"
   "*Template for `emerge-combine-versions' to combine the two versions.
 The template is inserted as a string, with the following interpolations:
        %a      the A version of the difference
@@ -232,7 +310,9 @@ The template is inserted as a string, with the following interpolations:
        %%      the character `%'
 Don't forget to end the template with a newline.
 Note that this variable can be made local to a particular merge buffer by
-giving a prefix argument to `emerge-set-combine-versions-template'.")
+giving a prefix argument to `emerge-set-combine-versions-template'."
+  :type 'string
+  :group 'emerge)
 
 ;; Build keymaps
 
@@ -245,9 +325,20 @@ must be prefixed by \\<emerge-fast-keymap>\\[emerge-basic-keymap] in `edit' mode
   "Local keymap used in Emerge `fast' mode.
 Makes Emerge commands directly available.")
 
-(defvar emerge-command-prefix "\C-c\C-c"
+(defvar emerge-options-menu
+  (make-sparse-keymap "Options"))
+
+(defvar emerge-merge-menu
+  (make-sparse-keymap "Merge"))
+
+(defvar emerge-move-menu
+  (make-sparse-keymap "Move"))
+
+(defcustom emerge-command-prefix "\C-c\C-c"
   "*Command prefix for Emerge commands in `edit' mode.
-Must be set before Emerge is loaded.")
+Must be set before Emerge is loaded."
+  :type 'string
+  :group 'emerge)
 
 ;; This function sets up the fixed keymaps.  It is executed when the first
 ;; Emerge is done to allow the user maximum time to set up the global keymap.
@@ -308,7 +399,83 @@ Must be set before Emerge is loaded.")
   (substitute-key-definition 'write-file 'emerge-query-write-file
                             emerge-fast-keymap (current-global-map))
   (substitute-key-definition 'save-buffer 'emerge-query-save-buffer
-                            emerge-fast-keymap (current-global-map)))
+                            emerge-fast-keymap (current-global-map))
+
+  (define-key emerge-basic-keymap [menu-bar] (make-sparse-keymap))
+
+  (define-key emerge-fast-keymap [menu-bar options]
+    (cons "Options" emerge-options-menu))
+  (define-key emerge-fast-keymap [menu-bar merge]
+    (cons "Merge" emerge-merge-menu))
+  (define-key emerge-fast-keymap [menu-bar move]
+    (cons "Move" emerge-move-menu))
+
+  (define-key emerge-move-menu [emerge-scroll-reset]
+    '("Scroll Reset" . emerge-scroll-reset))
+  (define-key emerge-move-menu [emerge-scroll-right]
+    '("Scroll Right" . emerge-scroll-right))
+  (define-key emerge-move-menu [emerge-scroll-left]
+    '("Scroll Left" . emerge-scroll-left))
+  (define-key emerge-move-menu [emerge-scroll-down]
+    '("Scroll Down" . emerge-scroll-down))
+  (define-key emerge-move-menu [emerge-scroll-up]
+    '("Scroll Up" . emerge-scroll-up))
+  (define-key emerge-move-menu [emerge-recenter]
+    '("Recenter" . emerge-recenter))
+  (define-key emerge-move-menu [emerge-mark-difference]
+    '("Mark Difference" . emerge-mark-difference))
+  (define-key emerge-move-menu [emerge-jump-to-difference]
+    '("Jump To Difference" . emerge-jump-to-difference))
+  (define-key emerge-move-menu [emerge-find-difference]
+    '("Find Difference" . emerge-find-difference))
+  (define-key emerge-move-menu [emerge-previous-difference]
+    '("Previous Difference" . emerge-previous-difference))
+  (define-key emerge-move-menu [emerge-next-difference]
+    '("Next Difference" . emerge-next-difference))
+
+
+  (define-key emerge-options-menu [emerge-one-line-window]
+    '("One Line Window" . emerge-one-line-window))
+  (define-key emerge-options-menu [emerge-set-merge-mode]
+    '("Set Merge Mode" . emerge-set-merge-mode))
+  (define-key emerge-options-menu [emerge-set-combine-template]
+    '("Set Combine Template..." . emerge-set-combine-template))
+  (define-key emerge-options-menu [emerge-default-B]
+    '("Default B" . emerge-default-B))
+  (define-key emerge-options-menu [emerge-default-A]
+    '("Default A" . emerge-default-A))
+  (define-key emerge-options-menu [emerge-skip-prefers]
+    '("Skip Prefers" . emerge-skip-prefers))
+  (define-key emerge-options-menu [emerge-auto-advance]
+    '("Auto Advance" . emerge-auto-advance))
+  (define-key emerge-options-menu [emerge-edit-mode]
+    '("Edit Mode" . emerge-edit-mode))
+  (define-key emerge-options-menu [emerge-fast-mode]
+    '("Fast Mode" . emerge-fast-mode))
+
+  (define-key emerge-merge-menu [emerge-abort] '("Abort" . emerge-abort))
+  (define-key emerge-merge-menu [emerge-quit] '("Quit" . emerge-quit))
+  (define-key emerge-merge-menu [emerge-split-difference]
+    '("Split Difference" . emerge-split-difference))
+  (define-key emerge-merge-menu [emerge-join-differences]
+    '("Join Differences" . emerge-join-differences))
+  (define-key emerge-merge-menu [emerge-trim-difference]
+    '("Trim Difference" . emerge-trim-difference))
+  (define-key emerge-merge-menu [emerge-combine-versions]
+    '("Combine Versions" . emerge-combine-versions))
+  (define-key emerge-merge-menu [emerge-copy-as-kill-B]
+    '("Copy B as Kill" . emerge-copy-as-kill-B))
+  (define-key emerge-merge-menu [emerge-copy-as-kill-A]
+    '("Copy A as Kill" . emerge-copy-as-kill-A))
+  (define-key emerge-merge-menu [emerge-insert-B]
+    '("Insert B" . emerge-insert-B))
+  (define-key emerge-merge-menu [emerge-insert-A]
+    '("Insert A" . emerge-insert-A))
+  (define-key emerge-merge-menu [emerge-select-B]
+    '("Select B" . emerge-select-B))
+  (define-key emerge-merge-menu [emerge-select-A]
+    '("Select A" . emerge-select-A)))
+
 
 ;; Variables which control each merge.  They are local to the merge buffer.
 
@@ -724,17 +891,16 @@ This is *not* a user option, since Emerge uses it for its own processing.")
    (let (f)
      (list current-prefix-arg
           (setq f (emerge-read-file-name "File A to merge" emerge-last-dir-A
-                                         nil nil))
-          (emerge-read-file-name "File B to merge" emerge-last-dir-B nil f)
+                                         nil nil t))
+          (emerge-read-file-name "File B to merge" emerge-last-dir-B nil f t)
           (and current-prefix-arg
                (emerge-read-file-name "Output file" emerge-last-dir-output
-                                      f f)))))
+                                      f f nil)))))
+  (if file-out
+      (add-hook 'quit-hooks (` (lambda () (emerge-files-exit (, file-out))))))
   (emerge-files-internal
    file-A file-B startup-hooks
-   (if file-out
-       (cons (` (lambda () (emerge-files-exit (, file-out))))
-            quit-hooks)
-     quit-hooks)
+   quit-hooks
    file-out))
 
 ;;;###autoload
@@ -745,19 +911,18 @@ This is *not* a user option, since Emerge uses it for its own processing.")
    (let (f)
      (list current-prefix-arg
           (setq f (emerge-read-file-name "File A to merge" emerge-last-dir-A
-                                         nil nil))
-          (emerge-read-file-name "File B to merge" emerge-last-dir-B nil f)
+                                         nil nil t))
+          (emerge-read-file-name "File B to merge" emerge-last-dir-B nil f t)
           (emerge-read-file-name "Ancestor file" emerge-last-dir-ancestor
-                                 nil f)
+                                 nil f t)
           (and current-prefix-arg
                (emerge-read-file-name "Output file" emerge-last-dir-output
-                                      f f)))))
+                                      f f nil)))))
+  (if file-out
+      (add-hook 'quit-hooks (` (lambda () (emerge-files-exit (, file-out))))))
   (emerge-files-with-ancestor-internal
    file-A file-B file-ancestor startup-hooks
-   (if file-out
-       (cons (` (lambda () (emerge-files-exit (, file-out))))
-            quit-hooks)
-     quit-hooks)
+   quit-hooks
    file-out))
 
 ;; Write the merge buffer out in place of the file the A buffer is visiting.
@@ -867,7 +1032,7 @@ This is *not* a user option, since Emerge uses it for its own processing.")
   (setq emerge-file-out file-out)
   (emerge-files-internal
    file-a file-b nil
-   (list (` (lambda () (emerge-remote-exit (, file-out) '(, exit-func)))))
+   (list (` (lambda () (emerge-remote-exit (, file-out) '(, emerge-exit-func)))))
    file-out)
   (throw 'client-wait nil))
 
@@ -876,17 +1041,18 @@ This is *not* a user option, since Emerge uses it for its own processing.")
   (setq emerge-file-out file-out)
   (emerge-files-with-ancestor-internal
    file-a file-b file-anc nil
-   (list (` (lambda () (emerge-remote-exit (, file-out) '(, exit-func)))))
+   (list (` (lambda () (emerge-remote-exit (, file-out) '(, emerge-exit-func)))))
    file-out)
   (throw 'client-wait nil))
 
-(defun emerge-remote-exit (file-out exit-func)
+(defun emerge-remote-exit (file-out emerge-exit-func)
   (emerge-write-and-delete file-out)
   (kill-buffer emerge-merge-buffer)
-  (funcall exit-func (if emerge-prefix-argument 1 0)))
+  (funcall emerge-exit-func (if emerge-prefix-argument 1 0)))
 
 ;;; Functions to start Emerge on RCS versions
 
+;;;###autoload
 (defun emerge-revisions (arg file revision-A revision-B
                         &optional startup-hooks quit-hooks)
   "Emerge two RCS revisions of a file."
@@ -906,6 +1072,7 @@ This is *not* a user option, since Emerge uses it for its own processing.")
             quit-hooks)
      quit-hooks)))
 
+;;;###autoload
 (defun emerge-revisions-with-ancestor (arg file revision-A
                                           revision-B ancestor
                                           &optional
@@ -1118,9 +1285,12 @@ Otherwise, the A or B file present is copied to the output file."
 
 ;;; Sample function for creating information for emerge-execute-line
 
-(defvar emerge-merge-directories-filename-regexp "[^.]"
-  "Regexp describing files to be processed by `emerge-merge-directories'.")
+(defcustom emerge-merge-directories-filename-regexp "[^.]"
+  "Regexp describing files to be processed by `emerge-merge-directories'."
+  :type 'regexp
+  :group 'emerge)
 
+;;;###autoload
 (defun emerge-merge-directories (a-dir b-dir ancestor-dir output-dir)
   (interactive 
    (list
@@ -1241,6 +1411,16 @@ Otherwise, the A or B file present is copied to the output file."
   ;; Install the Emerge commands
   (emerge-force-define-key emerge-edit-keymap emerge-command-prefix
                           'emerge-basic-keymap)
+  (define-key emerge-edit-keymap [menu-bar] (make-sparse-keymap))
+
+  ;; Create the additional menu bar items.
+  (define-key emerge-edit-keymap [menu-bar options]
+    (cons "Options" emerge-options-menu))
+  (define-key emerge-edit-keymap [menu-bar merge]
+    (cons "Merge" emerge-merge-menu))
+  (define-key emerge-edit-keymap [menu-bar move]
+    (cons "Move" emerge-move-menu))
+
   ;; Suppress write-file and save-buffer
   (substitute-key-definition 'write-file
                             'emerge-query-write-file
@@ -1292,6 +1472,12 @@ These characteristics are restored by `emerge-restore-buffer-characteristics'."
                           (emerge-restore-variables emerge-saved-variables
                                                     B-values))))
 
+;; Move to line DESIRED-LINE assuming we are at line CURRENT-LINE.
+;; Return DESIRED-LINE.
+(defun emerge-goto-line (desired-line current-line)
+  (forward-line (- desired-line current-line))
+  desired-line)
+
 (defun emerge-convert-diffs-to-markers (A-buffer
                                        B-buffer
                                        merge-buffer
@@ -1299,17 +1485,13 @@ These characteristics are restored by `emerge-restore-buffer-characteristics'."
   (let* (marker-list
         (A-point-min (emerge-eval-in-buffer A-buffer (point-min)))
         (offset (1- A-point-min))
-        (A-hidden-lines (emerge-eval-in-buffer
-                         A-buffer
-                         (save-restriction
-                           (widen)
-                           (count-lines 1 A-point-min))))
         (B-point-min (emerge-eval-in-buffer B-buffer (point-min)))
-        (B-hidden-lines (emerge-eval-in-buffer
-                         B-buffer
-                         (save-restriction
-                           (widen)
-                           (count-lines 1 B-point-min)))))
+        ;; Record current line number in each buffer
+        ;; so we don't have to count from the beginning.
+        (a-line 1)
+        (b-line 1))
+    (emerge-eval-in-buffer A-buffer (goto-char (point-min)))
+    (emerge-eval-in-buffer B-buffer (goto-char (point-min)))
     (while lineno-list
       (let* ((list-element (car lineno-list))
             a-begin-marker
@@ -1326,15 +1508,15 @@ These characteristics are restored by `emerge-restore-buffer-characteristics'."
        ;; place markers at the appropriate places in the buffers
        (emerge-eval-in-buffer
         A-buffer
-        (goto-line (+ a-begin A-hidden-lines))
+        (setq a-line (emerge-goto-line a-begin a-line))
         (setq a-begin-marker (point-marker))
-        (goto-line (+ a-end A-hidden-lines))
+        (setq a-line (emerge-goto-line a-end a-line))
         (setq a-end-marker (point-marker)))
        (emerge-eval-in-buffer
         B-buffer
-        (goto-line (+ b-begin B-hidden-lines))
+        (setq b-line (emerge-goto-line b-begin b-line))
         (setq b-begin-marker (point-marker))
-        (goto-line (+ b-end B-hidden-lines))
+        (setq b-line (emerge-goto-line b-end b-line))
         (setq b-end-marker (point-marker)))
        (setq merge-begin-marker (set-marker
                                  (make-marker)
@@ -1412,8 +1594,8 @@ With an argument, reestablish the default three-window display."
       (let* ((merge-buffer emerge-merge-buffer)
             (buffer-A emerge-A-buffer)
             (buffer-B emerge-B-buffer)
-            (window-A (get-buffer-window buffer-A))
-            (window-B (get-buffer-window buffer-B))
+            (window-A (get-buffer-window buffer-A 'visible))
+            (window-B (get-buffer-window buffer-B 'visible))
             (merge-window (get-buffer-window merge-buffer))
             (diff-vector
              (aref emerge-difference-list emerge-current-difference)))
@@ -1457,8 +1639,8 @@ With an argument, reestablish the default three-window display."
   (let* ((merge-buffer emerge-merge-buffer)
         (buffer-A emerge-A-buffer)
         (buffer-B emerge-B-buffer)
-        (window-A (get-buffer-window buffer-A))
-        (window-B (get-buffer-window buffer-B))
+        (window-A (get-buffer-window buffer-A 'visible))
+        (window-B (get-buffer-window buffer-B 'visible))
         (merge-window (get-buffer-window merge-buffer)))
     (if window-A (progn
                   (select-window window-A)
@@ -1597,7 +1779,7 @@ to the left margin, if they are in windows."
 ;; If there are min-lines lines above and below the region, then don't do
 ;; anything.
 ;; If not, recenter the region to make it so.
-;; If that isn't possible, remove context lines balancedly from top and botton
+;; If that isn't possible, remove context lines balancedly from top and bottom
 ;; so the entire region shows.
 ;; If that isn't possible, show the top of the region.
 ;; BEG must be at the beginning of a line.
@@ -1798,10 +1980,10 @@ which there is no preference."
                (emerge-select-A)
                (aset diff-vector 6 'default-A))))
        (setq n (1+ n))
-       (if (= (* (/ n 10) 10) n)
+       (if (zerop (% n 10))
            (message "Setting default to A...%d" n)))
       (emerge-unselect-and-select-difference selected-difference)))
-  (message "Default A set"))
+  (message "Default choice is now A"))
 
 (defun emerge-default-B ()
   "Make the B variant the default from here down.
@@ -1820,10 +2002,10 @@ which there is no preference."
                (emerge-select-B)
                (aset diff-vector 6 'default-B))))
        (setq n (1+ n))
-       (if (= (* (/ n 10) 10) n)
+       (if (zerop (% n 10))
            (message "Setting default to B...%d" n)))
       (emerge-unselect-and-select-difference selected-difference)))
-  (message "Default B set"))
+  (message "Default choice is now B"))
 
 (defun emerge-fast-mode ()
   "Set fast mode, for Emerge.
@@ -1836,8 +2018,7 @@ need not be prefixed with \\<emerge-fast-keymap>\\[emerge-basic-keymap]."
   (setq emerge-fast-mode t)
   (setq emerge-edit-mode nil)
   (message "Fast mode set")
-  ;; force mode line redisplay
-  (set-buffer-modified-p (buffer-modified-p)))
+  (force-mode-line-update))
 
 (defun emerge-edit-mode ()
   "Set edit mode, for Emerge.
@@ -1850,8 +2031,7 @@ must be prefixed with \\<emerge-fast-keymap>\\[emerge-basic-keymap]."
   (setq emerge-fast-mode nil)
   (setq emerge-edit-mode t)
   (message "Edit mode set")
-  ;; force mode line redisplay
-  (set-buffer-modified-p (buffer-modified-p)))
+  (force-mode-line-update))
 
 (defun emerge-auto-advance (arg)
   "Toggle Auto-Advance mode, for Emerge.
@@ -1863,11 +2043,10 @@ With a negative argument, turn off Auto-Advance mode."
   (setq emerge-auto-advance (if (null arg)
                                (not emerge-auto-advance)
                              (> (prefix-numeric-value arg) 0)))
-  (message (if emerge-skip-prefers
+  (message (if emerge-auto-advance
               "Auto-advance set"
             "Auto-advance cleared"))
-  ;; force mode line redisplay
-  (set-buffer-modified-p (buffer-modified-p)))
+  (force-mode-line-update))
 
 (defun emerge-skip-prefers (arg)
   "Toggle Skip-Prefers mode, for Emerge.
@@ -1882,8 +2061,7 @@ With a negative argument, turn off Skip-Prefers mode."
   (message (if emerge-skip-prefers
               "Skip-prefers set"
             "Skip-prefers cleared"))
-  ;; force mode line redisplay
-  (set-buffer-modified-p (buffer-modified-p)))
+  (force-mode-line-update))
 
 (defun emerge-copy-as-kill-A ()
   "Put the A variant of this difference in the kill ring."
@@ -2004,7 +2182,10 @@ Use C-u l to reset the windows afterward."
                                       (princ "Ancestor buffer is: ")
                                       (princ (buffer-name))))
                                   (princ "\n")))
-      (princ emerge-output-description))))
+      (princ emerge-output-description)
+      (save-excursion
+       (set-buffer standard-output)
+       (help-mode)))))
 
 (defun emerge-join-differences (arg)
   "Join the selected difference with the following one.
@@ -2215,6 +2396,7 @@ ancestor version does not share.)"
 If there is no containing difference and the prefix argument is positive,
 it finds the nearest following difference.  A negative prefix argument finds
 the nearest previous difference."
+  (interactive "P")
   (cond ((eq (current-buffer) emerge-A-buffer)
         (emerge-find-difference-A arg))
        ((eq (current-buffer) emerge-B-buffer)
@@ -2325,10 +2507,24 @@ merge buffers."
              (setq temp (- temp emerge-after-flag-lines)))))
     temp))
 
+(defun emerge-set-combine-template (string &optional localize)
+  "Set `emerge-combine-versions-template' to STRING.
+This value controls how `emerge-combine-versions' combines the two versions.
+With prefix argument, `emerge-combine-versions-template' is made local to this
+merge buffer.  Localization is permanent for any particular merge buffer."
+  (interactive "s\nP")
+  (if localize
+      (make-local-variable 'emerge-combine-versions-template))
+  (setq emerge-combine-versions-template string)
+  (message
+   (if (assq 'emerge-combine-versions-template (buffer-local-variables))
+       "emerge-set-combine-versions-template set locally"
+     "emerge-set-combine-versions-template set")))
+
 (defun emerge-set-combine-versions-template (start end &optional localize)
   "Copy region into `emerge-combine-versions-template'.
 This controls how `emerge-combine-versions' will combine the two versions.
-With prefix argument, `emerge-combine-versions' is made local to this
+With prefix argument, `emerge-combine-versions-template' is made local to this
 merge buffer.  Localization is permanent for any particular merge buffer."
   (interactive "r\nP")
   (if localize
@@ -2575,7 +2771,7 @@ keymap.  Leaves merge in fast mode."
 ;; Read a file name, handling all of the various defaulting rules.
 
 (defun emerge-read-file-name (prompt alternative-default-dir default-file
-                             A-file)
+                             A-file must-match)
   ;; `prompt' should not have trailing ": ", so that it can be modified
   ;; according to context.
   ;; If alternative-default-dir is non-nil, it should be used as the default
@@ -2603,7 +2799,7 @@ keymap.  Leaves merge in fast mode."
                    alternative-default-dir
                    (concat alternative-default-dir
                            (file-name-nondirectory A-file))
-                   'confirm))
+                   (and must-match 'confirm)))
    ;; If there is a default file, use it.
    (default-file
      (read-file-name (format "%s (default %s): " prompt default-file)
@@ -2612,7 +2808,7 @@ keymap.  Leaves merge in fast mode."
                     ;; Emerge as the default for this argument.
                     (and emerge-default-last-directories
                          alternative-default-dir)
-                    default-file 'confirm))
+                    default-file (and must-match 'confirm)))
    (t
     (read-file-name (concat prompt ": ")
                    ;; If emerge-default-last-directories is set, use the
@@ -2620,7 +2816,7 @@ keymap.  Leaves merge in fast mode."
                    ;; Emerge as the default for this argument.
                    (and emerge-default-last-directories
                         alternative-default-dir)
-                   nil 'confirm))))
+                   nil (and must-match 'confirm)))))
 
 ;; Revise the mode line to display which difference we have selected
 
@@ -2641,8 +2837,7 @@ keymap.  Leaves merge in fast mode."
                                       (prefer-B . " - B*")
                                       (combined . " - comb"))))
                        ""))))
-  ;; Force mode-line redisplay
-  (set-buffer-modified-p (buffer-modified-p)))
+  (force-mode-line-update))
 
 ;; compare two regions in two buffers for containing the same text
 (defun emerge-compare-buffers (buffer-x x-begin x-end buffer-y y-begin y-end)
@@ -2690,7 +2885,7 @@ keymap.  Leaves merge in fast mode."
 ;; a list of variables.  The argument is a list of symbols (the names of
 ;; the variables).  A list element can also be a list of two functions,
 ;; the first of which (when called with no arguments) gets the value, and
-;; the second (when called with a value as an argment) sets the value.
+;; the second (when called with a value as an argument) sets the value.
 ;; A "function" is anything that funcall can handle as an argument.
 
 (defun emerge-save-variables (vars)
@@ -2830,6 +3025,9 @@ If some prefix of KEY has a non-prefix definition, it is redefined."
 ;;                            minor-mode indicator))
 ;;             (princ (documentation minor-mode)))))
 ;;     (setq minor-modes (cdr minor-modes))))
+;;    (save-excursion
+;;      (set-buffer standard-output)
+;;      (help-mode))
 ;;    (print-help-return-message)))
 
 ;; This goes with the redefinition of describe-mode.
@@ -2996,9 +3194,11 @@ See also `auto-save-file-name-p'."
 
 ;; Metacharacters that have to be protected from the shell when executing
 ;; a diff/diff3 command.
-(defvar emerge-metachars "[ \t\n!\"#$&'()*;<=>?[\\^`{|~]"
+(defcustom emerge-metachars "[ \t\n!\"#$&'()*;<=>?[\\^`{|~]"
   "Characters that must be quoted with \\ when used in a shell command line.
-More precisely, a [...] regexp to match any one such character.")
+More precisely, a [...] regexp to match any one such character."
+  :type 'regexp
+  :group 'emerge)
 
 ;; Quote metacharacters (using \) when executing a diff/diff3 command.
 (defun emerge-protect-metachars (s)