Wednesday, March 2, 2011

region-or-buffer (revisited)

In Writing Emacs Commands That Work on Regions or the Entire Buffer I blogged about an Emacs macro (with-region-or-buffer) that I use to allow my Emacs commands to work on the current region if one exists or the entire buffer if not. I've been using that macro for some time now and it works perfectly for me. It probably works perfectly for almost everyone but there is an edge case: It implicitly assumes that transient-mark-mode is set. I've always set it explicitly in my .emacs file and as of Emacs 23 it's on by default. There are occasions, however, when one might want to have it off (see Fixing the Mark Commands in Transient Mark Mode for some examples and work arounds in the Mastering Emacs blog).

This came to my attention while I was browsing in Xah Lee's excellent Emacs Blog. He recommends using the function region-active-p, which is merely (and transient-mark-mode mark-active). That takes care of the edge case and for the sake of portability I decided to update with-region-or-buffer to use it. In the process of doing that update, however, I discovered that the recommended solution is to use use-region-p, which asks the additional question of whether the region is empty and use-empty-active-region is nil. Since there's no point in acting on an empty region, using this predicate will cause the action to be applied to the whole buffer instead.

With this in mind, the with-region-or-buffer macro is now

(defmacro with-region-or-buffer (args &rest body)
  "Execute BODY with BEG and END bound to the beginning and end of the
current region if one exists or the current buffer if not.
This macro replaces similar code using (interactive \"r\"), which
can fail when there is no mark set.

\(fn (BEG END) BODY...)"
  `(let ((,(car args) (if (use-region-p) (region-beginning) (point-min)))
         (,(cadr args) (if (use-region-p) (region-end) (point-max))))
     ,@body))

No comments:

Post a Comment