Function: smerge-refine-regions

smerge-refine-regions is an autoloaded and byte-compiled function defined in smerge-mode.el.gz.

Signature

(smerge-refine-regions BEG1 END1 BEG2 END2 PROPS-C &optional PREPROC PROPS-R PROPS-A)

Documentation

Show fine differences in the two regions BEG1..END1 and BEG2..END2.

PROPS-C is an alist of properties to put (via overlays) on the changes. PROPS-R is an alist of properties to put on removed characters. PROPS-A is an alist of properties to put on added characters. If PROPS-R and PROPS-A are nil, put PROPS-C on all changes. If PROPS-C is nil, but PROPS-R and PROPS-A are non-nil, put PROPS-A on added characters, PROPS-R on removed characters. If PROPS-C, PROPS-R and PROPS-A are non-nil, put PROPS-C on changed characters, PROPS-A on added characters, and PROPS-R on removed characters.

If non-nil, PREPROC is called with no argument in a buffer that contains a copy of a region, just before preparing it to for diff. It can be used to replace chars to try and eliminate some spurious differences.

Probably introduced at or before Emacs version 26.1.

Aliases

smerge-refine-subst (obsolete since 26.1)

Source Code

;; Defined in /usr/src/emacs/lisp/vc/smerge-mode.el.gz
;;;###autoload
(defun smerge-refine-regions (beg1 end1 beg2 end2 props-c &optional preproc props-r props-a)
  "Show fine differences in the two regions BEG1..END1 and BEG2..END2.
PROPS-C is an alist of properties to put (via overlays) on the changes.
PROPS-R is an alist of properties to put on removed characters.
PROPS-A is an alist of properties to put on added characters.
If PROPS-R and PROPS-A are nil, put PROPS-C on all changes.
If PROPS-C is nil, but PROPS-R and PROPS-A are non-nil,
put PROPS-A on added characters, PROPS-R on removed characters.
If PROPS-C, PROPS-R and PROPS-A are non-nil, put PROPS-C on changed characters,
PROPS-A on added characters, and PROPS-R on removed characters.

If non-nil, PREPROC is called with no argument in a buffer that contains
a copy of a region, just before preparing it to for `diff'.  It can be
used to replace chars to try and eliminate some spurious differences."
  (let* ((pos (point))
         deactivate-mark         ; The code does not modify any visible buffer.
         (file1 (make-temp-file "diff1"))
         (file2 (make-temp-file "diff2"))
         (smerge--refine-long-words
          (if smerge-refine-weight-hack (make-hash-table :test #'equal))))

    ;; Cover the two regions with one `smerge--refine-region' overlay each.
    (let ((ol1 (make-overlay beg1 end1 nil
                             ;; Make it shrink rather than spread when editing.
                             'front-advance nil))
          (ol2 (make-overlay beg2 end2 nil
                             ;; Make it shrink rather than spread when editing.
                             'front-advance nil))
          (common-props '((evaporate . t) (smerge--refine-region . t)
                          (cursor-sensor-functions
                           smerge--refine-shadow-cursor))))
      (when smerge-refine-shadow-cursor
        (cursor-sensor-mode 1))
      (dolist (prop (or props-a props-c))
        (when (and (not (memq (car prop) '(face font-lock-face)))
                   (member prop (or props-r props-c))
                   (or (not (and props-c props-a props-r))
                       (member prop props-c)))
          ;; This PROP is shared among all those overlays.
          ;; Better keep it also for the `smerge--refine-region' overlays,
          ;; so the client package recognizes them as being part of the
          ;; refinement (e.g. it will hopefully delete them like the others).
          (push prop common-props)))
      (dolist (prop common-props)
        (overlay-put ol1 (car prop) (cdr prop))
        (overlay-put ol2 (car prop) (cdr prop))))

    (unless (markerp beg1) (setq beg1 (copy-marker beg1)))
    (unless (markerp beg2) (setq beg2 (copy-marker beg2)))
    (let ((write-region-inhibit-fsync t)) ; Don't fsync temp files (Bug#12747).
      ;; Chop up regions into smaller elements and save into files.
      (smerge--refine-chopup-region beg1 end1 file1 preproc)
      (smerge--refine-chopup-region beg2 end2 file2 preproc))

    ;; Call diff on those files.
    (unwind-protect
        (with-temp-buffer
          ;; Allow decoding the EOL format, as on MS-Windows the Diff
          ;; utility might produce CR-LF EOLs.
          (let ((coding-system-for-read 'utf-8-emacs))
            (call-process diff-command nil t nil
                          (if (and smerge-refine-ignore-whitespace
                                   (not smerge-refine-weight-hack))
                              ;; Pass -a so diff treats it as a text file even
                              ;; if it contains \0 and such.
                              ;; Pass -d so as to get the smallest change, but
                              ;; also and more importantly because otherwise it
                              ;; may happen that diff doesn't behave like
                              ;; smerge-refine-weight-hack expects it to.
                              ;; See https://lists.gnu.org/r/emacs-devel/2007-11/msg00401.html
                              "-awd" "-ad")
                          file1 file2))
          ;; Process diff's output.
          (goto-char (point-min))
          (let ((last1 nil)
                (last2 nil))
            (while (not (eobp))
              (if (not (looking-at "\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)?\\([acd]\\)\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)?$"))
                  (error "Unexpected patch hunk header: %s"
                         (buffer-substring (point) (line-end-position))))
              (let ((op (char-after (match-beginning 3)))
                    (m1 (match-string 1))
                    (m2 (match-string 2))
                    (m4 (match-string 4))
                    (m5 (match-string 5)))
                (setq last1
                      (smerge--refine-highlight-change
		       beg1 m1 (if (eq op ?a) t m2)
		       ;; Try to use props-c only for changed chars,
		       ;; fallback to props-r for changed/removed chars,
		       ;; but if props-r is nil then fallback to props-c.
		       (or (and (eq op '?c) props-c) props-r props-c)))
                (setq last2
                      (smerge--refine-highlight-change
		       beg2 m4 (if (eq op ?d) t m5)
		       ;; Same logic as for removed chars above.
		       (or (and (eq op '?c) props-c) props-a props-c))))
              (overlay-put last1 'smerge--refine-other last2)
              (overlay-put last2 'smerge--refine-other last1)
              (forward-line 1)                            ;Skip hunk header.
              (and (re-search-forward "^[0-9]" nil 'move) ;Skip hunk body.
                   (goto-char (match-beginning 0))))
            ;; (cl-assert (or (null last1) (< (overlay-start last1) end1)))
            ;; (cl-assert (or (null last2) (< (overlay-start last2) end2)))
            (if smerge-refine-weight-hack
                (progn
                  ;; (cl-assert (or (null last1) (<= (overlay-end last1) end1)))
                  ;; (cl-assert (or (null last2) (<= (overlay-end last2) end2)))
                  )
              ;; smerge-refine-forward-function when calling in chopup may
              ;; have stopped because it bumped into EOB whereas in
              ;; smerge-refine-weight-hack it may go a bit further.
              (if (and last1 (> (overlay-end last1) end1))
                  (move-overlay last1 (overlay-start last1) end1))
              (if (and last2 (> (overlay-end last2) end2))
                  (move-overlay last2 (overlay-start last2) end2))
              )))
      (goto-char pos)
      (delete-file file1)
      (delete-file file2))))