Better Highlighting for the Emacs Region

The default highlighting of the Emacs “region” (the part of a buffer between “point” and “mark” — selected text, if any, in other words) is, out of the box, barely visible, at least against a light background. An attempt to customize it runs into an interesting problem when used in a text- or terminal-based environment, rather than a GUI.

Standard Faces

Emacs handles highlighting via text properties, or “faces”. According to the reference: “a face is a collection of graphical attributes for displaying text”, such as font, foreground and background color, etc. (Again, check the reference for the full list of attributes.) The function M-x describe-face can be used to get the full list of attributes (and their values) for any “face”.

Emacs defines a collection of standard faces; modes may define additional ones. The default face is the one used when nothing else applies; the region face is the one we are interested in: it controls the display of selected text.

Running M-x describe-face for the default face (on a typical Linux set-up) reveals the following attributes (among others):

       Foreground: black
DistantForeground: unspecified
       Background: white

Very simple: black text on white background. (The mysteriously named DistantForeground is used when the normal foreground color is too close (in color space) to the momentary background color.) For the region face, however, we find this:

       Foreground: unspecified
DistantForeground: gtk_selection_fg_color
       Background: gtk_selection_bg_color

What this means is that the region takes its colors from the current GTK+ theme, where available.

Choosing Custom Colors

This implies that there are two ways to customize the region highlighting: modify the GTK theme, or set a new color within Emacs. Since the idea is to modify how Emacs displays selected text, rather than changing the appearance of the overall desktop environment, we choose the latter.

(set-face-attribute 'region nil :background "light grey")

This is pretty self-explanatory; the nil argument indicates that the choice should apply in all Emacs frames.

Detecting Whether Emacs is Running in a Terminal

This customization works great if Emacs is running in GUI. But every once in a while, I run Emacs in a terminal (using emacs -nw — I won’t touch vi). But now the fixed color choice becomes a problem, because in a terminal, the default colors are different, and the specific, fixed color for the region is not appropriate. Is it possible to apply our customization only when Emacs is running in a GUI window?

In fact, it is easy. Emacs provides a predicate function (a function returning a Boolean value) that indicates whether we are running in a GUI window. So, the lines to include in one’s .emacs file might be:

(if (display-graphic-p)
    (set-face-attribute 'region nil :background "light grey"))

Now, the customization only takes effect in a GUI; if Emacs is running in a terminal, it will exhibit default behavior (which is fine for a terminal).

Highlighting a Rectangle as Region

Emacs allows operations on “rectangles”, which are rectangular sections of text, spanning multiple lines (rather than operating on lines). The rectangle concept seems to be rarely found outside of Emacs. It is one of these features that one doesn’t need all the time, but that appear like sheer magic in the right circumstances. Read more Emacs rectangles them in the Emacs reference.

Unfortunately, the key bindings for rectangle operations are a bit clumsy. However, there is a little known minor mode, rectangle-mark-mode, that is entered by setting the mark using C-x SPC (rather than the common C-SPC). When this minor mode is active, not only do regular Emacs kill and yank commands operate on a rectangle, but in addition the rectangular extent of the region is highlighted explicitly.

Can we customize our region highlighting command even further, to use a different color for rectangles than for regular text?

Sure we can…

(add-hook 'rectangle-mark-mode-hook
	  (lambda ()
	    (if (display-graphic-p)
		(set-face-attribute 'region nil :background
				    (if rectangle-mark-mode
					"orange" "light grey")))))