X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/e3f1bc6b8c8dc52af083d444b76d7c512a6d8e8d..d4b9aae98f4f5298eaef096e42ac6aca02d9ee31:/lisp/ediff-diff.el diff --git a/lisp/ediff-diff.el b/lisp/ediff-diff.el index c484f22433..0a3f6920e2 100644 --- a/lisp/ediff-diff.el +++ b/lisp/ediff-diff.el @@ -1,8 +1,9 @@ ;;; ediff-diff.el --- diff-related utilities -;; Copyright (C) 1994, 1995, 1996, 1997 Free Software Foundation, Inc. +;; Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, +;; 2002, 2003, 2004, 2005 Free Software Foundation, Inc. -;; Author: Michael Kifer +;; Author: Michael Kifer ;; This file is part of GNU Emacs. @@ -18,8 +19,10 @@ ;; 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. +;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +;; Boston, MA 02110-1301, USA. + +;;; Commentary: ;;; Code: @@ -27,6 +30,8 @@ ;; compiler pacifier (defvar ediff-default-variant) +(defvar null-device) +(defvar longlines-mode) (eval-when-compile (let ((load-path (cons (expand-file-name ".") load-path))) @@ -40,10 +45,59 @@ (require 'ediff-init) (defgroup ediff-diff nil - "Diff related utilities" + "Diff related utilities." :prefix "ediff-" :group 'ediff) +;; these two must be here to prevent ediff-test-utility from barking +(defcustom ediff-diff-program "diff" + "*Program to use for generating the differential of the two files." + :type 'string + :group 'ediff-diff) +(defcustom ediff-diff3-program "diff3" + "*Program to be used for three-way comparison. +Must produce output compatible with Unix's diff3 program." + :type 'string + :group 'ediff-diff) + + +;; The following functions must precede all defcustom-defined variables. + +;; The following functions needed for setting diff/diff3 options +;; test if diff supports the --binary option +(defsubst ediff-test-utility (diff-util option &optional files) + (eq 0 (apply 'call-process + (append (list diff-util nil nil nil option) files)))) + +(defun ediff-diff-mandatory-option (diff-util) + (let ((file (if (boundp 'null-device) null-device "/dev/null"))) + (cond ((not (memq system-type '(ms-dos windows-nt windows-95))) + "") + ((and (string= diff-util ediff-diff-program) + (ediff-test-utility + ediff-diff-program "--binary" (list file file))) + "--binary") + ((and (string= diff-util ediff-diff3-program) + (ediff-test-utility + ediff-diff3-program "--binary" (list file file file))) + "--binary") + (t "")))) + +;; make sure that mandatory options are added even if the user changes +;; ediff-diff-options or ediff-diff3-options in the customization widget +(defun ediff-reset-diff-options (symb val) + (let* ((diff-program + (if (eq symb 'ediff-diff-options) + ediff-diff-program + ediff-diff3-program)) + (mandatory-option (ediff-diff-mandatory-option diff-program)) + (spacer (if (string-equal mandatory-option "") "" " "))) + (set symb + (if (string-match mandatory-option val) + val + (concat mandatory-option spacer val))) + )) + (defcustom ediff-shell (cond ((eq system-type 'emx) "cmd") ; OS/2 @@ -51,11 +105,12 @@ shell-file-name) ; no standard name on MS-DOS ((memq system-type '(vax-vms axp-vms)) "*dcl*") ; VMS (t "sh")) ; UNIX - "*The shell used to run diff and patch. If user's .profile or -.cshrc files are set up correctly, any shell will do. However, some people -set $prompt or other things incorrectly, which leads to undesirable output -messages. These may cause Ediff to fail. In such a case, set ediff-shell -to a shell that you are not using or, better, fix your shell's startup file." + "*The shell used to run diff and patch. +If user's .profile or .cshrc files are set up correctly, any shell +will do. However, some people set $prompt or other things +incorrectly, which leads to undesirable output messages. These may +cause Ediff to fail. In such a case, set `ediff-shell' to a shell that +you are not using or, better, fix your shell's startup file." :type 'string :group 'ediff-diff) @@ -65,15 +120,19 @@ It must return code 0, if its arguments are identical files." :type 'string :group 'ediff-diff) -(defcustom ediff-diff-program "diff" - "*Program to use for generating the differential of the two files." - :type 'string +(defcustom ediff-cmp-options nil + "*Options to pass to `ediff-cmp-program'. +If GNU diff is used as `ediff-cmp-program', then the most useful options +are `-I REGEXP', to ignore changes whose lines match the REGEXP." + :type '(repeat string) :group 'ediff-diff) -(defcustom ediff-diff-options "" - "*Options to pass to `ediff-diff-program'. -If diff\(1\) is used as `ediff-diff-program', then the most useful options are + +(defcustom ediff-diff-options "" + "*Options to pass to `ediff-diff-program'. +If Unix diff is used as `ediff-diff-program', then the most useful options are `-w', to ignore space, and `-i', to ignore case of letters. At present, the option `-c' is not allowed." + :set 'ediff-reset-diff-options :type 'string :group 'ediff-diff) @@ -89,15 +148,11 @@ This output is not used by Ediff internally." ;;; Support for diff3 -(defvar ediff-match-diff3-line "^====\\(.?\\)$" +(defvar ediff-match-diff3-line "^====\\(.?\\)\C-m?$" "Pattern to match lines produced by diff3 that describe differences.") -(defcustom ediff-diff3-program "diff3" - "*Program to be used for three-way comparison. -Must produce output compatible with Unix's diff3 program." - :type 'string - :group 'ediff-diff) -(defcustom ediff-diff3-options "" +(defcustom ediff-diff3-options "" "*Options to pass to `ediff-diff3-program'." + :set 'ediff-reset-diff-options :type 'string :group 'ediff-diff) (defcustom ediff-diff3-ok-lines-regexp @@ -111,8 +166,8 @@ Lines that do not match are assumed to be error messages." ;; the status can be =diff(A), =diff(B), or =diff(A+B) (ediff-defvar-local ediff-diff-status "" "") - -;;; Fine differences + +;;; Fine differences (ediff-defvar-local ediff-auto-refine (if (ediff-has-face-support-p) 'on 'nix) "If `on', Ediff auto-highlights fine diffs for the current diff region. @@ -129,10 +184,10 @@ Use `setq-default' if setting it in .emacs") (ediff-defvar-local ediff-auto-refine-limit 1400 "*Auto-refine only the regions of this size \(in bytes\) or less.") - + ;;; General -(defvar ediff-diff-ok-lines-regexp +(defvar ediff-diff-ok-lines-regexp (concat "^\\(" "[0-9,]+[acd][0-9,]+\C-m?$" @@ -162,7 +217,7 @@ The function should take three mandatory arguments, file-A, file-B, and file-C. It may ignore file C for diff2 jobs. It should also take one optional arguments, diff-number to refine.") - + ;;; Functions ;; Generate the difference vector and overlays for the two files @@ -172,9 +227,10 @@ one optional arguments, diff-number to refine.") ;; ediff-setup-diff-regions-function, which can also have the value ;; ediff-setup-diff-regions3, which takes 4 arguments. (defun ediff-setup-diff-regions (file-A file-B file-C) - (if (string-match "c" ediff-diff-options) + ;; looking either for '-c' or a 'c' in a set of clustered non-long options + (if (string-match "^-c\\| -c\\|-[^- ]+c" ediff-diff-options) (error "Option `-c' is not allowed in `ediff-diff-options'")) - + ;; create, if it doesn't exist (or (ediff-buffer-live-p ediff-diff-buffer) (setq ediff-diff-buffer @@ -187,6 +243,7 @@ one optional arguments, diff-number to refine.") ;; Run the diff program on FILE1 and FILE2 and put the output in DIFF-BUFFER ;; Return the size of DIFF-BUFFER +;; The return code isn't used in the program at present. (defun ediff-make-diff2-buffer (diff-buffer file1 file2) (let ((file1-size (ediff-file-size file1)) (file2-size (ediff-file-size file2))) @@ -202,19 +259,6 @@ one optional arguments, diff-number to refine.") (sit-for 2) ;; 1 is an error exit code 1) - ((< file1-size 0) - (message "Can't diff remote files: %s" - (ediff-abbreviate-file-name file1)) - (sit-for 2) - ;; 1 is an error exit code - 1) - ((< file2-size 0) - (message "Can't diff remote file: %s" - (ediff-abbreviate-file-name file2)) - (sit-for 2) - (message "") - ;; 1 is an error exit code - 1) (t (message "Computing differences between %s and %s ..." (file-name-nondirectory file1) (file-name-nondirectory file2)) @@ -226,9 +270,9 @@ one optional arguments, diff-number to refine.") (message "") (ediff-with-current-buffer diff-buffer (buffer-size)))))) - - + + ;; If file-A/B/C is nil, do 2-way comparison with the non-nil buffers ;; This function works for diff3 and diff2 jobs (defun ediff-setup-fine-diff-regions (file-A file-B file-C reg-num) @@ -236,7 +280,7 @@ one optional arguments, diff-number to refine.") (setq ediff-fine-diff-buffer (get-buffer-create (ediff-unique-buffer-name "*ediff-fine-diff" "*")))) - + (let (diff3-job diff-program diff-options ok-regexp diff-list) (setq diff3-job ediff-3way-job diff-program (if diff3-job ediff-diff3-program ediff-diff-program) @@ -244,7 +288,7 @@ one optional arguments, diff-number to refine.") ok-regexp (if diff3-job ediff-diff3-ok-lines-regexp ediff-diff-ok-lines-regexp)) - + (ediff-message-if-verbose "Refining difference region %d ..." (1+ reg-num)) (ediff-exec-process diff-program ediff-fine-diff-buffer 'synchronize diff-options @@ -256,12 +300,12 @@ one optional arguments, diff-number to refine.") (if diff3-job (if file-C file-C file-B)) ) ; exec process - + (ediff-prepare-error-list ok-regexp ediff-fine-diff-buffer) (ediff-message-if-verbose "") ;; "Refining difference region %d ... done" (1+ reg-num)) - + (setq diff-list (if diff3-job (ediff-extract-diffs3 @@ -270,26 +314,26 @@ one optional arguments, diff-number to refine.") ;; fixup diff-list (if diff3-job (cond ((not file-A) - (mapcar (function (lambda (elt) - (aset elt 0 nil) - (aset elt 1 nil))) + (mapcar (lambda (elt) + (aset elt 0 nil) + (aset elt 1 nil)) (cdr diff-list))) ((not file-B) - (mapcar (function (lambda (elt) - (aset elt 2 nil) - (aset elt 3 nil))) + (mapcar (lambda (elt) + (aset elt 2 nil) + (aset elt 3 nil)) (cdr diff-list))) ((not file-C) - (mapcar (function (lambda (elt) - (aset elt 4 nil) - (aset elt 5 nil))) + (mapcar (lambda (elt) + (aset elt 4 nil) + (aset elt 5 nil)) (cdr diff-list))) )) - + (ediff-convert-fine-diffs-to-overlays diff-list reg-num) )) - - + + (defun ediff-prepare-error-list (ok-regexp diff-buff) (or (ediff-buffer-live-p ediff-error-buffer) (setq ediff-error-buffer @@ -309,7 +353,7 @@ one optional arguments, diff-number to refine.") (ediff-skip-unsuitable-frames) (switch-to-buffer error-buf) (ediff-kill-buffer-carefully ctl-buf) - (error "Errors in diff output. Diff output is in %S" diff-buff)))) + (error "Errors in diff output. Diff output is in %S" diff-buff)))) ;; BOUNDS specifies visibility bounds to use. ;; WORD-MODE tells whether we are in the word-mode or not. @@ -326,7 +370,7 @@ one optional arguments, diff-number to refine.") (c-prev 1) diff-list shift-A shift-B ) - + ;; diff list contains word numbers, unless changed later (setq diff-list (cons (if word-mode 'words 'points) diff-list)) @@ -335,10 +379,10 @@ one optional arguments, diff-number to refine.") (setq shift-A (ediff-overlay-start (ediff-get-value-according-to-buffer-type 'A bounds)) - shift-B + shift-B (ediff-overlay-start (ediff-get-value-according-to-buffer-type 'B bounds)))) - + ;; reset point in buffers A/B/C (ediff-with-current-buffer A-buffer (goto-char (if shift-A shift-A (point-min)))) @@ -347,24 +391,24 @@ one optional arguments, diff-number to refine.") (if (ediff-buffer-live-p C-buffer) (ediff-with-current-buffer C-buffer (goto-char (point-min)))) - + (ediff-with-current-buffer diff-buffer (goto-char (point-min)) (while (re-search-forward ediff-match-diff-line nil t) - (let* ((a-begin (string-to-int (buffer-substring (match-beginning 1) - (match-end 1)))) + (let* ((a-begin (string-to-number (buffer-substring (match-beginning 1) + (match-end 1)))) (a-end (let ((b (match-beginning 3)) (e (match-end 3))) (if b - (string-to-int (buffer-substring b e)) + (string-to-number (buffer-substring b e)) a-begin))) (diff-type (buffer-substring (match-beginning 4) (match-end 4))) - (b-begin (string-to-int (buffer-substring (match-beginning 5) - (match-end 5)))) + (b-begin (string-to-number (buffer-substring (match-beginning 5) + (match-end 5)))) (b-end (let ((b (match-beginning 7)) (e (match-end 7))) (if b - (string-to-int (buffer-substring b e)) + (string-to-number (buffer-substring b e)) b-begin))) a-begin-pt a-end-pt b-begin-pt b-end-pt c-begin c-end c-begin-pt c-end-pt) @@ -381,17 +425,17 @@ one optional arguments, diff-number to refine.") ;; (string-equal diff-type "c") (setq a-end (1+ a-end) b-end (1+ b-end)))) - + (if (eq ediff-default-variant 'default-B) (setq c-begin b-begin c-end b-end) (setq c-begin a-begin c-end a-end)) - + ;; compute main diff vector (if word-mode ;; make diff-list contain word numbers - (setq diff-list + (setq diff-list (nconc diff-list (list (if (ediff-buffer-live-p C-buffer) @@ -417,25 +461,47 @@ one optional arguments, diff-number to refine.") c-prev c-end) ;; else convert lines to points (ediff-with-current-buffer A-buffer - (forward-line (- a-begin a-prev)) - (setq a-begin-pt (point)) - (forward-line (- a-end a-begin)) - (setq a-end-pt (point) - a-prev a-end)) + (let ((longlines-mode-val + (if (and (boundp 'longlines-mode) longlines-mode) 1 0))) + ;; we must disable and then restore longlines-mode + (if (eq longlines-mode-val 1) + (longlines-mode 0)) + (forward-line (- a-begin a-prev)) + (setq a-begin-pt (point)) + (forward-line (- a-end a-begin)) + (setq a-end-pt (point) + a-prev a-end) + (if (eq longlines-mode-val 1) + (longlines-mode longlines-mode-val)) + )) (ediff-with-current-buffer B-buffer - (forward-line (- b-begin b-prev)) - (setq b-begin-pt (point)) - (forward-line (- b-end b-begin)) - (setq b-end-pt (point) - b-prev b-end)) + (let ((longlines-mode-val + (if (and (boundp 'longlines-mode) longlines-mode) 1 0))) + (if (eq longlines-mode-val 1) + (longlines-mode 0)) + (forward-line (- b-begin b-prev)) + (setq b-begin-pt (point)) + (forward-line (- b-end b-begin)) + (setq b-end-pt (point) + b-prev b-end) + (if (eq longlines-mode-val 1) + (longlines-mode longlines-mode-val)) + )) (if (ediff-buffer-live-p C-buffer) (ediff-with-current-buffer C-buffer - (forward-line (- c-begin c-prev)) - (setq c-begin-pt (point)) - (forward-line (- c-end c-begin)) - (setq c-end-pt (point) - c-prev c-end))) - (setq diff-list + (let ((longlines-mode-val + (if (and (boundp 'longlines-mode) longlines-mode) 1 0))) + (if (eq longlines-mode-val 1) + (longlines-mode 0)) + (forward-line (- c-begin c-prev)) + (setq c-begin-pt (point)) + (forward-line (- c-end c-begin)) + (setq c-end-pt (point) + c-prev c-end) + (if (eq longlines-mode-val 1) + (longlines-mode longlines-mode-val)) + ))) + (setq diff-list (nconc diff-list (list @@ -456,13 +522,13 @@ one optional arguments, diff-number to refine.") nil nil ; dummy ancestor nil nil ; dummy state of diff & merge nil ; dummy state of ancestor - ))) + ))) ))) - + ))) ; end ediff-with-current-buffer diff-list )) - + (defun ediff-convert-diffs-to-overlays (diff-list) (ediff-set-diff-overlays-in-one-buffer 'A diff-list) @@ -475,22 +541,24 @@ one optional arguments, diff-number to refine.") (if ediff-merge-job (setq ediff-state-of-merge (vconcat - (mapcar (function - (lambda (elt) - (let ((state-of-merge (aref elt 9)) - (state-of-ancestor (aref elt 10))) - (vector - (if state-of-merge (format "%S" state-of-merge)) - state-of-ancestor)))) + (mapcar (lambda (elt) + (let ((state-of-merge (aref elt 9)) + (state-of-ancestor (aref elt 10))) + (vector + ;; state of merge: prefers/default-A/B or combined + (if state-of-merge (format "%S" state-of-merge)) + ;; whether the ancestor region is empty + state-of-ancestor))) ;; the first elt designates type of list (cdr diff-list)) ))) (message "Processing difference regions ... done")) - + (defun ediff-set-diff-overlays-in-one-buffer (buf-type diff-list) (let* ((current-diff -1) (buff (ediff-get-buffer buf-type)) + (ctl-buf ediff-control-buffer) ;; ediff-extract-diffs puts the type of diff-list as the first elt ;; of this list. The type is either 'points or 'words (diff-list-type (car diff-list)) @@ -498,17 +566,17 @@ one optional arguments, diff-number to refine.") (ediff-get-value-according-to-buffer-type buf-type ediff-narrow-bounds))) (limit (ediff-overlay-end - (ediff-get-value-according-to-buffer-type + (ediff-get-value-according-to-buffer-type buf-type ediff-narrow-bounds))) diff-overlay-list list-element total-diffs begin end pt-saved overlay state-of-diff) (setq diff-list (cdr diff-list)) ; discard diff list type (setq total-diffs (length diff-list)) - + ;; shift, if necessary (ediff-with-current-buffer buff (setq pt-saved shift)) - + (while diff-list (setq current-diff (1+ current-diff) list-element (car diff-list) @@ -522,7 +590,7 @@ one optional arguments, diff-number to refine.") (t 7))) ; Ancestor state-of-diff (aref list-element 8) ) - + (cond ((and (not (eq buf-type state-of-diff)) (not (eq buf-type 'Ancestor)) (memq state-of-diff '(A B C))) @@ -531,19 +599,20 @@ one optional arguments, diff-number to refine.") (setq state-of-diff (format "=diff(%S)" state-of-diff)) ) (t (setq state-of-diff nil))) - + ;; Put overlays at appropriate places in buffer ;; convert word numbers to points, if necessary (if (eq diff-list-type 'words) (progn (ediff-with-current-buffer buff (goto-char pt-saved)) - (setq begin (ediff-goto-word (1+ begin) buff) - end (ediff-goto-word end buff 'end)) + (ediff-with-current-buffer ctl-buf + (setq begin (ediff-goto-word (1+ begin) buff) + end (ediff-goto-word end buff 'end))) (if (> end limit) (setq end limit)) (if (> begin end) (setq begin end)) (setq pt-saved (ediff-with-current-buffer buff (point))))) (setq overlay (ediff-make-bullet-proof-overlay begin end buff)) - + (ediff-overlay-put overlay 'priority ediff-shadow-overlay-priority) (ediff-overlay-put overlay 'ediff-diff-num current-diff) (if (and (ediff-has-face-support-p) @@ -554,9 +623,11 @@ one optional arguments, diff-number to refine.") (if (= 0 (mod current-diff 10)) (message "Buffer %S: Processing difference region %d of %d" buf-type current-diff total-diffs)) - ;; record all overlays for this difference - ;; the second elt, nil, is a place holder for the fine diff vector. - ;; the third elt, nil, is a place holder for no-fine-diffs flag. + ;; Record all overlays for this difference. + ;; The 2-d elt, nil, is a place holder for the fine diff vector. + ;; The 3-d elt, nil, is a place holder for no-fine-diffs flag. + ;; The 4-th elt says which diff region is different from the other two + ;; (3-way jobs only). (setq diff-overlay-list (nconc diff-overlay-list @@ -564,7 +635,7 @@ one optional arguments, diff-number to refine.") diff-list (cdr diff-list)) ) ; while - + (set (ediff-get-symbol-from-alist buf-type ediff-difference-vector-alist) (vconcat diff-overlay-list)) )) @@ -573,16 +644,16 @@ one optional arguments, diff-number to refine.") ;; if `flag' is 'noforce then make fine-diffs only if this region's fine ;; diffs have not been computed before. ;; if `flag' is 'skip then don't compute fine diffs for this region. -(defun ediff-make-fine-diffs (&optional n flag) +(defun ediff-make-fine-diffs (&optional n flag) (or n (setq n ediff-current-difference)) - + (if (< ediff-number-of-differences 1) (error ediff-NO-DIFFERENCES)) - + (if ediff-word-mode (setq flag 'skip ediff-auto-refine 'nix)) - + (or (< n 0) (>= n ediff-number-of-differences) ;; n is within the range @@ -597,7 +668,7 @@ one optional arguments, diff-number to refine.") (whitespace-B (ediff-whitespace-diff-region-p n 'B)) (whitespace-C (ediff-whitespace-diff-region-p n 'C)) cumulative-fine-diff-length) - + (cond ;; If one of the regions is empty (or 2 in 3way comparison) ;; then don't refine. ;; If the region happens to be entirely whitespace or empty then @@ -609,13 +680,13 @@ one optional arguments, diff-number to refine.") (if ediff-3way-comparison-job (ediff-message-if-verbose "Region %d is empty in all buffers but %S" - (1+ n) + (1+ n) (cond ((not empty-A) 'A) ((not empty-B) 'B) ((not empty-C) 'C))) (ediff-message-if-verbose "Region %d in buffer %S is empty" - (1+ n) + (1+ n) (cond (empty-A 'A) (empty-B 'B) (empty-C 'C))) @@ -646,7 +717,7 @@ one optional arguments, diff-number to refine.") (or (ediff-get-fine-diff-vector n 'A) (memq ediff-auto-refine '(off nix)) (ediff-message-if-verbose - "Region %d exceeds auto-refine limit. Type `%s' to refine" + "Region %d exceeds the auto-refinement limit. Type `%s' to refine" (1+ n) (substitute-command-keys "\\[ediff-make-or-kill-fine-diffs]") @@ -661,7 +732,7 @@ one optional arguments, diff-number to refine.") ediff-control-buffer) (setq file-A (ediff-make-temp-file tmp-buffer "fineDiffA" file-A)) - + (ediff-wordify (ediff-get-diff-posn 'B 'beg n) (ediff-get-diff-posn 'B 'end n) @@ -670,7 +741,7 @@ one optional arguments, diff-number to refine.") ediff-control-buffer) (setq file-B (ediff-make-temp-file tmp-buffer "fineDiffB" file-B)) - + (if ediff-3way-job (progn (ediff-wordify @@ -682,12 +753,12 @@ one optional arguments, diff-number to refine.") (setq file-C (ediff-make-temp-file tmp-buffer "fineDiffC" file-C)))) - + ;; save temp file names. (setq ediff-temp-file-A file-A ediff-temp-file-B file-B ediff-temp-file-C file-C) - + ;; set the new vector of fine diffs, if none exists (cond ((and ediff-3way-job whitespace-A) (ediff-setup-fine-diff-regions nil file-B file-C n)) @@ -700,7 +771,7 @@ one optional arguments, diff-number to refine.") (ediff-setup-fine-diff-regions file-A file-B nil n)) (t (ediff-setup-fine-diff-regions file-A file-B file-C n))) - + (setq cumulative-fine-diff-length (+ (length (ediff-get-fine-diff-vector n 'A)) (length (ediff-get-fine-diff-vector n 'B)) @@ -708,7 +779,7 @@ one optional arguments, diff-number to refine.") (if (and file-C (not ediff-merge-job)) (length (ediff-get-fine-diff-vector n 'C)) 0))) - + (cond ((or ;; all regions are white space (and whitespace-A whitespace-B whitespace-C) @@ -730,16 +801,19 @@ one optional arguments, diff-number to refine.") "in buffers A & C") (whitespace-C (ediff-mark-diff-as-space-only n 'C) "in buffers A & B")))) - (t + (t (ediff-mark-diff-as-space-only n nil))) ) ) ; end cond (ediff-set-fine-diff-properties n) ))) - + ;; Interface to ediff-make-fine-diffs. Checks for auto-refine limit, etc. (defun ediff-install-fine-diff-if-necessary (n) - (cond ((eq ediff-auto-refine 'on) + (cond ((and (eq ediff-auto-refine 'on) + ediff-use-faces + (not (eq ediff-highlighting-style 'off)) + (not (eq ediff-highlighting-style 'ascii))) (if (and (> ediff-auto-refine-limit (- (ediff-get-diff-posn 'A 'end n) @@ -749,12 +823,12 @@ one optional arguments, diff-number to refine.") (ediff-get-diff-posn 'B 'beg n)))) (ediff-make-fine-diffs n 'noforce) (ediff-make-fine-diffs n 'skip))) - + ;; highlight iff fine diffs already exist ((eq ediff-auto-refine 'off) (ediff-make-fine-diffs n 'skip)))) - - + + ;; if fine diff vector is not set for diff N, then do nothing (defun ediff-set-fine-diff-properties (n &optional default) (or (not (ediff-has-face-support-p)) @@ -766,11 +840,11 @@ one optional arguments, diff-number to refine.") (ediff-set-fine-diff-properties-in-one-buffer 'B n default) (if ediff-3way-job (ediff-set-fine-diff-properties-in-one-buffer 'C n default))))) - + (defun ediff-set-fine-diff-properties-in-one-buffer (buf-type n &optional default) (let ((fine-diff-vector (ediff-get-fine-diff-vector n buf-type)) - (face (if default + (face (if default 'default (face-name (ediff-get-symbol-from-alist @@ -784,35 +858,39 @@ one optional arguments, diff-number to refine.") ediff-current-diff-overlay-alist)) 'priority) 0))))) - (mapcar - (function (lambda (overl) - (ediff-set-overlay-face overl face) - (ediff-overlay-put overl 'priority priority))) - fine-diff-vector))) - -;; This assumes buffer C and that the region looks like a combination of -;; regions in buffer A and C. + (mapcar (lambda (overl) + (ediff-set-overlay-face overl face) + (ediff-overlay-put overl 'priority priority)) + fine-diff-vector))) + +;; Set overlays over the regions that denote delimiters (defun ediff-set-fine-overlays-for-combined-merge (diff-list reg-num) - (let (overlay1 overlay2 overlay3) - (setq overlay1 (ediff-make-bullet-proof-overlay (nth 0 diff-list) - (nth 1 diff-list) - ediff-buffer-C) - overlay2 (ediff-make-bullet-proof-overlay (nth 2 diff-list) - (nth 3 diff-list) - ediff-buffer-C) - overlay3 (ediff-make-bullet-proof-overlay (nth 4 diff-list) - (nth 5 diff-list) - ediff-buffer-C)) - (ediff-set-fine-diff-vector reg-num 'C (vector overlay1 overlay2 overlay3)) + (let (overlay overlay-list) + (while diff-list + (condition-case nil + (setq overlay + (ediff-make-bullet-proof-overlay + (nth 0 diff-list) (nth 1 diff-list) ediff-buffer-C)) + (error "")) + (setq overlay-list (cons overlay overlay-list)) + (if (> (length diff-list) 1) + (setq diff-list (cdr (cdr diff-list))) + (error "ediff-set-fine-overlays-for-combined-merge: corrupt list of +delimiter regions")) + ) + (setq overlay-list (reverse overlay-list)) + (ediff-set-fine-diff-vector + reg-num 'C (apply 'vector overlay-list)) )) - - + + ;; Convert diff list to overlays for a given DIFF-REGION ;; in buffer of type BUF-TYPE (defun ediff-set-fine-overlays-in-one-buffer (buf-type diff-list region-num) (let* ((current-diff -1) (reg-start (ediff-get-diff-posn buf-type 'beg region-num)) (buff (ediff-get-buffer buf-type)) + (ctl-buf ediff-control-buffer) combined-merge-diff-list diff-overlay-list list-element begin end overlay) @@ -820,7 +898,7 @@ one optional arguments, diff-number to refine.") (ediff-clear-fine-differences-in-one-buffer region-num buf-type) (setq diff-list (cdr diff-list)) ; discard list type (words or points) (ediff-with-current-buffer buff (goto-char reg-start)) - + ;; if it is a combined merge then set overlays in buff C specially (if (and ediff-merge-job (eq buf-type 'C) (setq combined-merge-diff-list @@ -841,21 +919,30 @@ one optional arguments, diff-number to refine.") () ; skip this diff ;; Put overlays at appropriate places in buffers ;; convert lines to points, if necessary - (setq begin (ediff-goto-word (1+ begin) buff) - end (ediff-goto-word end buff 'end)) + (ediff-with-current-buffer ctl-buf + (setq begin (ediff-goto-word (1+ begin) buff) + end (ediff-goto-word end buff 'end))) (setq overlay (ediff-make-bullet-proof-overlay begin end buff)) ;; record all overlays for this difference region (setq diff-overlay-list (nconc diff-overlay-list (list overlay)))) - + (setq diff-list (cdr diff-list)) ) ; while ;; convert the list of difference information into a vector ;; for fast access - (ediff-set-fine-diff-vector + (ediff-set-fine-diff-vector region-num buf-type (vconcat diff-overlay-list)) ))) +(defsubst ediff-convert-fine-diffs-to-overlays (diff-list region-num) + (ediff-set-fine-overlays-in-one-buffer 'A diff-list region-num) + (ediff-set-fine-overlays-in-one-buffer 'B diff-list region-num) + (if ediff-3way-job + (ediff-set-fine-overlays-in-one-buffer 'C diff-list region-num) + )) + + ;; Stolen from emerge.el (defun ediff-get-diff3-group (file) ;; This save-excursion allows ediff-get-diff3-group to be called for the @@ -864,23 +951,23 @@ one optional arguments, diff-number to refine.") ;; can produce the groups in the order 1, 2, 3 or 1, 3, 2. (save-excursion (re-search-forward - (concat "^" file ":\\([0-9]+\\)\\(,\\([0-9]+\\)\\)?\\([ac]\\)$")) + (concat "^" file ":\\([0-9]+\\)\\(,\\([0-9]+\\)\\)?\\([ac]\\)\C-m?$")) (beginning-of-line 2) ;; treatment depends on whether it is an "a" group or a "c" group (if (string-equal (buffer-substring (match-beginning 4) (match-end 4)) "c") ;; it is a "c" group (if (match-beginning 2) ;; it has two numbers - (list (string-to-int + (list (string-to-number (buffer-substring (match-beginning 1) (match-end 1))) - (1+ (string-to-int + (1+ (string-to-number (buffer-substring (match-beginning 3) (match-end 3))))) ;; it has one number - (let ((x (string-to-int + (let ((x (string-to-number (buffer-substring (match-beginning 1) (match-end 1))))) (list x (1+ x)))) ;; it is an "a" group - (let ((x (1+ (string-to-int + (let ((x (1+ (string-to-number (buffer-substring (match-beginning 1) (match-end 1)))))) (list x x))))) @@ -905,7 +992,7 @@ one optional arguments, diff-number to refine.") (anc-prev 1) diff-list shift-A shift-B shift-C ) - + ;; diff list contains word numbers or points, depending on word-mode (setq diff-list (cons (if word-mode 'words 'points) diff-list)) @@ -913,14 +1000,14 @@ one optional arguments, diff-number to refine.") (setq shift-A (ediff-overlay-start (ediff-get-value-according-to-buffer-type 'A bounds)) - shift-B + shift-B (ediff-overlay-start (ediff-get-value-according-to-buffer-type 'B bounds)) - shift-C + shift-C (if three-way-comp (ediff-overlay-start (ediff-get-value-according-to-buffer-type 'C bounds))))) - + ;; reset point in buffers A, B, C (ediff-with-current-buffer A-buffer (goto-char (if shift-A shift-A (point-min)))) @@ -932,7 +1019,7 @@ one optional arguments, diff-number to refine.") (if (ediff-buffer-live-p anc-buffer) (ediff-with-current-buffer anc-buffer (goto-char (point-min)))) - + (ediff-with-current-buffer diff-buffer (goto-char (point-min)) (while (re-search-forward ediff-match-diff3-line nil t) @@ -964,7 +1051,7 @@ one optional arguments, diff-number to refine.") b-begin-pt b-end-pt c-begin-pt c-end-pt anc-begin-pt anc-end-pt) - + (setq state-of-ancestor (= c-or-anc-begin c-or-anc-end)) @@ -977,11 +1064,11 @@ one optional arguments, diff-number to refine.") (t (setq c-begin a-begin c-end a-end))) - + ;; compute main diff vector (if word-mode ;; make diff-list contain word numbers - (setq diff-list + (setq diff-list (nconc diff-list (list (vector (- a-begin a-prev) (- a-end a-begin) @@ -997,31 +1084,60 @@ one optional arguments, diff-number to refine.") c-prev c-end) ;; else convert lines to points (ediff-with-current-buffer A-buffer - (forward-line (- a-begin a-prev)) - (setq a-begin-pt (point)) - (forward-line (- a-end a-begin)) - (setq a-end-pt (point) - a-prev a-end)) + (let ((longlines-mode-val + (if (and (boundp 'longlines-mode) longlines-mode) 1 0))) + ;; we must disable and then restore longlines-mode + (if (eq longlines-mode-val 1) + (longlines-mode 0)) + (forward-line (- a-begin a-prev)) + (setq a-begin-pt (point)) + (forward-line (- a-end a-begin)) + (setq a-end-pt (point) + a-prev a-end) + (if (eq longlines-mode-val 1) + (longlines-mode longlines-mode-val)) + )) (ediff-with-current-buffer B-buffer - (forward-line (- b-begin b-prev)) - (setq b-begin-pt (point)) - (forward-line (- b-end b-begin)) - (setq b-end-pt (point) - b-prev b-end)) + (let ((longlines-mode-val + (if (and (boundp 'longlines-mode) longlines-mode) 1 0))) + (if (eq longlines-mode-val 1) + (longlines-mode 0)) + (forward-line (- b-begin b-prev)) + (setq b-begin-pt (point)) + (forward-line (- b-end b-begin)) + (setq b-end-pt (point) + b-prev b-end) + (if (eq longlines-mode-val 1) + (longlines-mode longlines-mode-val)) + )) (ediff-with-current-buffer C-buffer - (forward-line (- c-begin c-prev)) - (setq c-begin-pt (point)) - (forward-line (- c-end c-begin)) - (setq c-end-pt (point) - c-prev c-end)) + (let ((longlines-mode-val + (if (and (boundp 'longlines-mode) longlines-mode) 1 0))) + (if (eq longlines-mode-val 1) + (longlines-mode 0)) + (forward-line (- c-begin c-prev)) + (setq c-begin-pt (point)) + (forward-line (- c-end c-begin)) + (setq c-end-pt (point) + c-prev c-end) + (if (eq longlines-mode-val 1) + (longlines-mode longlines-mode-val)) + )) (if (ediff-buffer-live-p anc-buffer) (ediff-with-current-buffer anc-buffer - (forward-line (- c-or-anc-begin anc-prev)) - (setq anc-begin-pt (point)) - (forward-line (- c-or-anc-end c-or-anc-begin)) - (setq anc-end-pt (point) - anc-prev c-or-anc-end))) - (setq diff-list + (let ((longlines-mode-val + (if (and (boundp 'longlines-mode) longlines-mode) 1 0))) + (if (eq longlines-mode-val 1) + (longlines-mode 0)) + (forward-line (- c-or-anc-begin anc-prev)) + (setq anc-begin-pt (point)) + (forward-line (- c-or-anc-end c-or-anc-begin)) + (setq anc-end-pt (point) + anc-prev c-or-anc-end) + (if (eq longlines-mode-val 1) + (longlines-mode longlines-mode-val)) + ))) + (setq diff-list (nconc diff-list ;; if comparing with ancestor, then there also is a @@ -1046,11 +1162,11 @@ one optional arguments, diff-number to refine.") ))) ))) )) - + ))) ; end ediff-with-current-buffer diff-list )) - + ;; Generate the difference vector and overlays for three files ;; File-C is either the third file to compare (in case of 3-way comparison) ;; or it is the ancestor file. @@ -1058,11 +1174,11 @@ one optional arguments, diff-number to refine.") (or (ediff-buffer-live-p ediff-diff-buffer) (setq ediff-diff-buffer (get-buffer-create (ediff-unique-buffer-name "*ediff-diff" "*")))) - + (message "Computing differences ...") (ediff-exec-process ediff-diff3-program ediff-diff-buffer 'synchronize ediff-diff3-options file-A file-B file-C) - + (ediff-prepare-error-list ediff-diff3-ok-lines-regexp ediff-diff-buffer) ;;(message "Computing differences ... done") (ediff-convert-diffs-to-overlays @@ -1070,7 +1186,7 @@ one optional arguments, diff-number to refine.") ediff-diff-buffer ediff-word-mode ediff-3way-comparison-job ediff-narrow-bounds) )) - + ;; Execute PROGRAM asynchronously, unless OS/2, Windows-*, or DOS, or unless ;; SYNCH is non-nil. BUFFER must be a buffer object, and must be alive. The @@ -1079,9 +1195,15 @@ one optional arguments, diff-number to refine.") ;; args. (defun ediff-exec-process (program buffer synch options &rest files) (let ((data (match-data)) + (coding-system-for-read ediff-coding-system-for-read) args) (setq args (append (split-string options) files)) (setq args (delete "" (delq nil args))) ; delete nil and "" from arguments + ;; the --binary option, if present, should be used only for buffer jobs + ;; or for refining the differences + (or (string-match "buffer" (symbol-name ediff-job-name)) + (eq buffer ediff-fine-diff-buffer) + (setq args (delete "--binary" args))) (unwind-protect (let ((directory default-directory) proc) @@ -1110,9 +1232,9 @@ one optional arguments, diff-number to refine.") (set-process-sentinel proc 'ediff-process-sentinel) (set-process-filter proc 'ediff-process-filter) ))) - (set-match-data data)))) - -;; This is shell-command-filter from simple.el in FSF Emacs. + (store-match-data data)))) + +;; This is shell-command-filter from simple.el in Emacs. ;; Copied here because XEmacs doesn't have it. (defun ediff-process-filter (proc string) ;; Do save-excursion by hand so that we can leave point numerically unchanged @@ -1135,7 +1257,7 @@ one optional arguments, diff-number to refine.") (if opoint (goto-char opoint)) (set-buffer obuf)))) - + ;; like shell-command-sentinel but doesn't print an exit status message ;; we do this because diff always exits with status 1, if diffs are found ;; so shell-command-sentinel displays a confusing message to the user @@ -1147,39 +1269,45 @@ one optional arguments, diff-number to refine.") (set-buffer (process-buffer process)) (setq mode-line-process nil)) (delete-process process)))) - -;;; Word functions used to refine the current diff + +;;; Word functions used to refine the current diff (defvar ediff-forward-word-function 'ediff-forward-word "*Function to call to move to the next word. Used for splitting difference regions into individual words.") +(make-variable-buffer-local 'ediff-forward-word-function) (defvar ediff-whitespace " \n\t\f" "*Characters constituting white space. These characters are ignored when differing regions are split into words.") +(make-variable-buffer-local 'ediff-whitespace) -;;(defvar ediff-word-1 "a-zA-Z---_`'.?!:" -(defvar ediff-word-1 "a-zA-Z---_" +(defvar ediff-word-1 + (ediff-cond-compile-for-xemacs-or-emacs "a-zA-Z---_" "-[:word:]_") "*Characters that constitute words of type 1. More precisely, [ediff-word-1] is a regexp that matches type 1 words. -See `ediff-forward-word' for more details.") +See `ediff-forward-word' for more details.") +(make-variable-buffer-local 'ediff-word-1) (defvar ediff-word-2 "0-9.," "*Characters that constitute words of type 2. More precisely, [ediff-word-2] is a regexp that matches type 2 words. See `ediff-forward-word' for more details.") +(make-variable-buffer-local 'ediff-word-2) (defvar ediff-word-3 "`'?!:;\"{}[]()" "*Characters that constitute words of type 3. More precisely, [ediff-word-3] is a regexp that matches type 3 words. See `ediff-forward-word' for more details.") +(make-variable-buffer-local 'ediff-word-3) (defvar ediff-word-4 (concat "^" ediff-word-1 ediff-word-2 ediff-word-3 ediff-whitespace) "*Characters that constitute words of type 4. More precisely, [ediff-word-4] is a regexp that matches type 4 words. -See `ediff-forward-word' for more details.") +See `ediff-forward-word' for more details.") +(make-variable-buffer-local 'ediff-word-4) ;; Split region along word boundaries. Each word will be on its own line. ;; Output to buffer out-buffer. @@ -1187,73 +1315,161 @@ See `ediff-forward-word' for more details.") "Move point one word forward. There are four types of words, each of which consists entirely of characters in `ediff-word-1', `ediff-word-2', `ediff-word-3', or -`ediff-word-4'. Words are recognized by passing these in turn as the -argument to `skip-chars-forward'." - (or (> (skip-chars-forward ediff-word-1) 0) +`ediff-word-4'. Words are recognized by passing these one after another as +arguments to `skip-chars-forward'." + (or (> (+ (skip-chars-forward ediff-word-1) + (skip-syntax-forward "w")) + 0) (> (skip-chars-forward ediff-word-2) 0) (> (skip-chars-forward ediff-word-3) 0) (> (skip-chars-forward ediff-word-4) 0) )) + (defun ediff-wordify (beg end in-buffer out-buffer &optional control-buf) - (let (sv-point string) + (let ((forward-word-function + ;; eval in control buf to let user create local versions for + ;; different invocations + (if control-buf + (ediff-with-current-buffer control-buf + ediff-forward-word-function) + ediff-forward-word-function)) + inbuf-syntax-tbl sv-point diff-string) (save-excursion (set-buffer in-buffer) - (setq string (buffer-substring-no-properties beg end)) + (setq inbuf-syntax-tbl + (if control-buf + (ediff-with-current-buffer control-buf + ediff-syntax-table) + (syntax-table))) + (setq diff-string (buffer-substring-no-properties beg end)) (set-buffer out-buffer) + ;; Make sure that temp buff syntax table is the same as the original buf + ;; syntax tbl, because we use ediff-forward-word in both and + ;; ediff-forward-word depends on the syntax classes of characters. + (set-syntax-table inbuf-syntax-tbl) (erase-buffer) - (insert string) + (insert diff-string) (goto-char (point-min)) (skip-chars-forward ediff-whitespace) (delete-region (point-min) (point)) - + (while (not (eobp)) - ;; eval incontrol buf to let user create local versions for - ;; different invocations - (if control-buf - (funcall - (ediff-with-current-buffer control-buf ediff-forward-word-function)) - (funcall ediff-forward-word-function)) + (funcall forward-word-function) (setq sv-point (point)) (skip-chars-forward ediff-whitespace) (delete-region sv-point (point)) (insert "\n"))))) - -;; copy string from BEG END from IN-BUF to OUT-BUF -(defun ediff-copy-to-buffer (beg end in-buffer out-buffer) - (let (string) - (save-excursion - (set-buffer in-buffer) - (setq string (buffer-substring beg end)) - (set-buffer out-buffer) - (erase-buffer) - (insert string) - (goto-char (point-min))))) +;; copy string specified as BEG END from IN-BUF to OUT-BUF +(defun ediff-copy-to-buffer (beg end in-buffer out-buffer) + (with-current-buffer out-buffer + (erase-buffer) + (insert-buffer-substring in-buffer beg end) + (goto-char (point-min)))) ;; goto word #n starting at current position in buffer `buf' -;; For ediff, a word is either a string of a-z,A-Z, incl `-' and `_'; -;; or a string of other non-blanks. A blank is a \n\t\f +;; For ediff, a word is determined by ediff-forward-word-function ;; If `flag' is non-nil, goto the end of the n-th word. (defun ediff-goto-word (n buf &optional flag) ;; remember val ediff-forward-word-function has in ctl buf - (let ((fwd-word-fun ediff-forward-word-function)) + (let ((fwd-word-fun ediff-forward-word-function) + (syntax-tbl ediff-syntax-table)) (ediff-with-current-buffer buf (skip-chars-forward ediff-whitespace) - (while (> n 1) - (funcall fwd-word-fun) - (skip-chars-forward ediff-whitespace) - (setq n (1- n))) - (if (and flag (> n 0)) - (funcall fwd-word-fun)) + (ediff-with-syntax-table syntax-tbl + (while (> n 1) + (funcall fwd-word-fun) + (skip-chars-forward ediff-whitespace) + (setq n (1- n))) + (if (and flag (> n 0)) + (funcall fwd-word-fun))) (point)))) (defun ediff-same-file-contents (f1 f2) - "T if F1 and F2 have identical contents." - (let ((res (call-process ediff-cmp-program nil nil nil f1 f2))) - (and (numberp res) (eq res 0)))) + "Return t if files F1 and F2 have identical contents." + (if (and (not (file-directory-p f1)) + (not (file-directory-p f2))) + (let ((res + (apply 'call-process ediff-cmp-program nil nil nil + (append ediff-cmp-options (list f1 f2))))) + (and (numberp res) (eq res 0)))) + ) + + +(defun ediff-same-contents (d1 d2 &optional filter-re) + "Returns t iff D1 and D2 have the same content. +D1 and D2 can either be both directories or both regular files. +Symlinks and the likes are not handled. +If FILTER-RE is non-nil, recursive checking in directories +affects only files whose names match the expression." + ;; Normalize empty filter RE to nil. + (unless (> (length filter-re) 0) (setq filter-re nil)) + ;; Indicate progress + (message "Comparing '%s' and '%s' modulo '%s'" d1 d2 filter-re) + (cond + ;; D1 & D2 directories => recurse + ((and (file-directory-p d1) + (file-directory-p d2)) + (if (null ediff-recurse-to-subdirectories) + (if (y-or-n-p "Compare subdirectories recursively? ") + (setq ediff-recurse-to-subdirectories 'yes) + (setq ediff-recurse-to-subdirectories 'no))) + (if (eq ediff-recurse-to-subdirectories 'yes) + (let* ((all-entries-1 (directory-files d1 t filter-re)) + (all-entries-2 (directory-files d2 t filter-re)) + (entries-1 (ediff-delete-all-matches "^\\.\\.?$" all-entries-1)) + (entries-2 (ediff-delete-all-matches "^\\.\\.?$" all-entries-2)) + ) + + (ediff-same-file-contents-lists entries-1 entries-2 filter-re) + )) + ) ; end of the directories case + ;; D1 & D2 are both files => compare directly + ((and (file-regular-p d1) + (file-regular-p d2)) + (ediff-same-file-contents d1 d2)) + ;; Otherwise => false: unequal contents + ) + ) + +;; If lists have the same length and names of files are pairwise equal +;; (removing the directories) then compare contents pairwise. +;; True if all contents are the same; false otherwise +(defun ediff-same-file-contents-lists (entries-1 entries-2 filter-re) + ;; First, check only the names (works quickly and ensures a + ;; precondition for subsequent code) + (if (and (= (length entries-1) (length entries-2)) + (equal (mapcar 'file-name-nondirectory entries-1) + (mapcar 'file-name-nondirectory entries-2))) + ;; With name equality established, compare the entries + ;; through recursion. + (let ((continue t)) + (while (and entries-1 continue) + (if (ediff-same-contents + (car entries-1) (car entries-2) filter-re) + (setq entries-1 (cdr entries-1) + entries-2 (cdr entries-2)) + (setq continue nil)) + ) + ;; if reached the end then lists are equal + (null entries-1)) + ) + ) + + +;; ARG1 is a regexp, ARG2 is a list of full-filenames +;; Delete all entries that match the regexp +(defun ediff-delete-all-matches (regex file-list-list) + (let (result elt) + (while file-list-list + (setq elt (car file-list-list)) + (or (string-match regex (file-name-nondirectory elt)) + (setq result (cons elt result))) + (setq file-list-list (cdr file-list-list))) + (reverse result))) ;;; Local Variables: @@ -1262,5 +1478,5 @@ argument to `skip-chars-forward'." ;;; eval: (put 'ediff-with-current-buffer 'edebug-form-spec '(form body)) ;;; End: - -;; ediff-diff.el ends here +;;; arch-tag: a86d448e-58d7-4572-a1d9-fdedfa22f648 +;;; ediff-diff.el ends here