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 about Emacs rectangles 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")))))