How do i check if two regions have identical content? Is it possible to do so while ignoring language-specific whitespace?
3 Answers
(As previous mentioned in the comments by glucas), a heavyweight approach to comparing regions is provided by the excellent ediff
library:
ediff-regions-linewise
ediff-regions-wordwise
These commands "run Ediff on a pair of regions in specified buffers".
They prompt you first to specify which buffer(s) the regions come from, and then to mark and/or confirm the region to use in each of those buffers in turn, and then finally the ediff session is started to compare the two.
Obviously you'd only use this approach when the benefits of using ediff outweighed the more-cumbersome initialisation procedure.
Interactively, I do this one of a few ways, depending on the scenario.
First, if the regions are small -- e.g., lately I've been comparing some test output with the expected results, and both are just a single line -- I just edit so the lines are next to each other and compare visually.
Another "low tech" solution is to use isearch. If both regions are visible at the same time, I use C-s
at the start of one region, then repeatedly use C-w
to pick up characters from the region. Then I watch the highlighting of the other region -- when it stops I have found the difference.
For larger regions, I use compare-windows
. This has a way to ignore whitespace. If the regions are in the same buffer, just split windows and move point in each window to the start of one of the regions before beginning.
On the other hand if you wanted to do this programmatically, I think the basic idea is to use buffer-substring
and then equal
(or string=
) to compare. I am not sure how you would ignore "language-specific whitespace", but I suppose one way would be to rewrite each string into a canonical form before comparing; for example by running a regular expression over them to strip such whitespace.
-
I'm glad you pointed those out. One man's "low tech" is another's "smart tech." Commented Sep 24, 2015 at 16:59
-
I concur with the
isearch
andcompare-windows
approaches :) I use both of those far more regularly than any more heavyweight approach.– philsCommented Sep 24, 2015 at 23:20
Since this solution uses only CLI Diff, it also answers your "ignore whitespace" question, tweak diff's option to do whatever you want. Please man diff
.
;; Diff two regions
;; Step 1: Select a region and `M-x diff-region-tag-selected-as-a'
;; Step 2: Select another region and `M-x diff-region-compare-with-b'
(defun diff-region-format-region-boundary (b e)
"Make sure lines are selected and B is less than E"
(let (tmp rlt)
;; swap b e, make sure b < e
(when (> b e)
(setq tmp b)
(setq b e)
(set e tmp))
;; select lines
(save-excursion
;; Another workaround for evil-visual-line bug:
;; In evil-mode, if we use hotkey V or `M-x evil-visual-line` to select line,
;; the (line-beginning-position) of the line which is after the last selected
;; line is always (region-end)! Don't know why.
(if (and (> e b)
(save-excursion (goto-char e) (= e (line-beginning-position)))
(boundp 'evil-state) (eq evil-state 'visual))
(setq e (1- e)))
(goto-char b)
(setq b (line-beginning-position))
(goto-char e)
(setq e (line-end-position)))
(setq rlt (list b e))
rlt))
(defun diff-region-tag-selected-as-a ()
"Select a region to compare"
(interactive)
(when (region-active-p)
(let (tmp buf)
;; select lines
(setq tmp (diff-region-format-region-boundary (region-beginning) (region-end)))
(setq buf (get-buffer-create "*Diff-regionA*"))
(save-current-buffer
(set-buffer buf)
(erase-buffer))
(append-to-buffer buf (car tmp) (cadr tmp))))
(message "Now select other region to compare and run `diff-region-compare-with-b`"))
(defun diff-region-compare-with-b ()
"Compare current region with region selected by `diff-region-tag-selected-as-a' "
(interactive)
(if (region-active-p)
(let (rlt-buf
diff-output
(fa (make-temp-file (expand-file-name "scor"
(or small-temporary-file-directory
temporary-file-directory))))
(fb (make-temp-file (expand-file-name "scor"
(or small-temporary-file-directory
temporary-file-directory)))))
(when fb
(setq tmp (diff-region-format-region-boundary (region-beginning) (region-end)))
(write-region (car tmp) (cadr tmp) fb))
(setq rlt-buf (get-buffer-create "*Diff-region-output*"))
(when (and fa (file-exists-p fa) fb (file-exists-p fb))
(save-current-buffer
(set-buffer (get-buffer-create "*Diff-regionA*"))
(write-region (point-min) (point-max) fa))
(setq diff-output (shell-command-to-string (format "diff -Nabur %s %s" fa fb)))
;; show the diff output
(if (string= diff-output "")
(message "Two regions are SAME!")
(save-current-buffer
(switch-to-buffer-other-window rlt-buf)
(set-buffer rlt-buf)
(erase-buffer)
(insert diff-output)
(diff-mode))))
(if (and fa (file-exists-p fa))
(delete-file fa))
(if (and fb (file-exists-p fb))
(delete-file fb)))
(message "Please select region at first!")))
ediff-regions-linewise
orediff-regions-wordwise
?compare-windows
to compare different regions of the same buffer. Either way,ediff
is what you really need.