How do i check if two regions have identical content? Is it possible to do so while ignoring language-specific whitespace?
3 Answers
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.

- 759
- 4
- 8
-
I'm glad you pointed those out. One man's "low tech" is another's "smart tech." – Emacs User Sep 24 '15 at 16:59
-
I concur with the `isearch` and `compare-windows` approaches :) I use both of those far more regularly than any more heavyweight approach. – phils Sep 24 '15 at 23:20
(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.

- 48,657
- 3
- 76
- 115
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!")))

- 4,781
- 18
- 36
(defun last-kills-identical () (interactive) (message (if (string= (first kill-ring) (second kill-ring)) "They are the same" "They are different")))
– Ben Hyde Aug 28 '15 at 13:35