Elisp: Beware of Region Boundary Change

By Xah Lee. Date: . Last updated: .

Got stung by region in emacs, wasted about 4 hours. Here's what i learned.

Whenever you work in a region, remember that the boundaries of the text that you are interested is changed when you add or remove text in that region. For example, suppose p1 and p2 is the boundary of some text you are interested. After doing some change there, suppose you want to do some more change. Don't just call (something-region p1 p2) again, because p2 is no longer the correct boundary.

Use save-restriction and narrow-to-region, like this:

  (narrow-to-region pos1 pos2)
  (something1-region (point-min) (point-max))
  (something2-region (point-min) (point-max))

Last week, i updated the article Elisp: Syntax Color Source Code in HTML and re-implemented several of my personal commands, and today found that it behaved incorrectly: it deleted text outside of a region!

Here's a function dehtmlize-span-region effected by this:

(defun dehtmlize-span-region (p1 p2)
  (interactive "r")
  (replace-regexp-pairs-region p1 p2 '(["<span class=\"[^\"]+\">" ""]))
  (replace-pairs-region p1 p2 '( ["</span>" ""] ["&amp;" "&"] ["&lt;" "<"] ["&gt;" ">"] ) )

Note how innocuous it looks. p1 and p2 are my region boundaries, and i work on them. Here's the correct code:

;; correct
(defun dehtmlize-span-region (p1 p2)
  (interactive "r")
      (narrow-to-region p1 p2)
      (replace-regexp-pairs-region (point-min) (point-max) '(["<span class=\"[^\"]+\">" ""]))
      (replace-pairs-region (point-min) (point-max) '( ["</span>" ""] ["&amp;" "&"] ["&lt;" "<"] ["&gt;" ">"] ) ) ) ) )

Looks so trivial a thing, but i spent 4 hours debugging to realize the fact that when i modified the text in region by my first function, the region boundary has changed. I remember, few years ago, i also spent half a day on region problems and wrongly concluded that narrow-to-region should be avoided in elisp program.