A Problem … and a Solution

You are given this (1)

a b c aaaa bb ccccc aaa bbb cccc aa bb ccc

How would you generate this

[ a , b , c ] [ aaaa, bb , ccccc ] [ aaa , bbb, cccc ] [ aa , bb , ccc ]

A answer is you use Emacs’ built-in rectangle commands.

[CLICK HERE FOR UNSCALED GIF]

[CLICK HERE FOR UNSCALED GIF]

What do rectangle commands do?

The Emacs manual says (1)

“Rectangle” commands operate on rectangular areas of the text: all the

characters between a certain pair of columns, in a certain range of

lines. They are useful with text in multi-column formats, and for

changing text into or out of such formats.

The above description is too terse, and doesn’t tell you what the commands do in a simple and relatable way. In my opinion,

rectangle commands are primitive tools to create pretty tables from a region of text and convert those tables in to different table formats like TSV (1), CSV (1), Org (1), LaTeX (1), GFM (1), (2) or any other makeshift format that you may come up with.

Note that rectangle commands are primitive i.e., you can create pretty tables, but NOT in a single-shot but in multiple steps.

Step 1: Create a menu

Copy the Emacs Lisp snippet (1) below to your .emacs and restart your Emacs .

(require 'easymenu) (dolist (item '((begin-tabify menu-item "--") ["Tabify" tabify :help "(tabify START END &optional ARG)



Convert multiple spaces in region to tabs when possible.

A group of spaces is partially replaced by tabs

when this can be done without changing the column they end at.

If called interactively with prefix ARG, convert for the entire

buffer.



Called non-interactively, the region is specified by arguments

START and END, rather than by the position of point and mark.

The variable `tab-width' controls the spacing of tab stops."] ["Untabify" untabify :help "(untabify START END &optional ARG)



Convert all tabs in region to multiple spaces, preserving columns.

If called interactively with prefix ARG, convert for the entire

buffer.



Called non-interactively, the region is specified by arguments

START and END, rather than by the position of point and mark.

The variable `tab-width' controls the spacing of tab stops."] (after-tabify menu-item "--"))) (easy-menu-add-item global-map '("menu-bar" "edit") item "bookmark")) (easy-menu-add-item global-map '("menu-bar" "edit") ["Rectangle Mark Mode" rectangle-mark-mode :style toggle :selected rectangle-mark-mode :help "(rectangle-mark-mode &optional ARG)



Toggle the region as rectangular.

Activates the region if needed. Only lasts until the region is deactivated."] "bookmark") (with-eval-after-load 'rect (easy-menu-define my-rectangle-mark-mode-map-menu rectangle-mark-mode-map "Menu for Rectangle Mark Mode Map." '("Rectangle" ["String Rectangle" string-rectangle :help "(string-rectangle START END STRING)



Replace rectangle contents with STRING on each line.

The length of STRING need not be the same as the rectangle width.



When called interactively and option `rectangle-preview' is

non-nil, display the result as the user enters the string into

the minibuffer.



Called from a program, takes three args; START, END and STRING."] ["Delete Rectangle" delete-rectangle :help "(delete-rectangle START END &optional FILL)



Delete (don't save) text in the region-rectangle.

The same range of columns is deleted in each line starting with the

line where the region begins and ending with the line where the region

ends.



When called from a program the rectangle's corners are START and END.

With a prefix (or a FILL) argument, also fill lines where nothing has

to be deleted."] "--" ["Kill Rectangle" kill-rectangle :help "(kill-rectangle START END &optional FILL)



Delete the region-rectangle and save it as the last killed one.



When called from a program the rectangle's corners are START and END.

You might prefer to use `delete-extract-rectangle' from a program.



With a prefix (or a FILL) argument, also fill lines where nothing has to be

deleted.



If the buffer is read-only, Emacs will beep and refrain from deleting

the rectangle, but put it in `killed-rectangle' anyway. This means that

you can use this command to copy text from a read-only buffer.

(If the variable `kill-read-only-ok' is non-nil, then this won't

even beep.)"] ["Copy Rectangle As Kill" copy-rectangle-as-kill :help "(copy-rectangle-as-kill START END)



Copy the region-rectangle and save it as the last killed one."] ["Yank Rectangle" yank-rectangle :help "(yank-rectangle)



Yank the last killed rectangle with upper left corner at point."] "--" ["Open Rectangle" open-rectangle :help "(open-rectangle START END &optional FILL)



Blank out the region-rectangle, shifting text right.



The text previously in the region is not overwritten by the blanks,

but instead winds up to the right of the rectangle.



When called from a program the rectangle's corners are START and END.

With a prefix (or a FILL) argument, fill with blanks even if there is

no text on the right side of the rectangle."] ["Close Rectangle" close-rectangle :help "(close-rectangle START END &optional FILL)



Delete all whitespace following a specified column in each line.

The left edge of the rectangle specifies the position in each line

at which whitespace deletion should begin. On each line in the

rectangle, all contiguous whitespace starting at that column is deleted.



When called from a program the rectangle's corners are START and END.

With a prefix (or a FILL) argument, also fill too short lines."] ["Clear Rectangle" clear-rectangle :help "(clear-rectangle START END &optional FILL)



Blank out the region-rectangle.

The text previously in the region is overwritten with blanks.



When called from a program the rectangle's corners are START and END.

With a prefix (or a FILL) argument, also fill with blanks the parts of the

rectangle which were empty."] "--" ["Rectangle Number Lines" rectangle-number-lines :help "(rectangle-number-lines START END START-AT &optional FORMAT)



Insert numbers in front of the region-rectangle.



START-AT, if non-nil, should be a number from which to begin

counting. FORMAT, if non-nil, should be a format string to pass

to `format' along with the line count. When called interactively

with a prefix argument, prompt for START-AT and FORMAT."] "--" ["Rectangle Exchange Point And Mark" rectangle-exchange-point-and-mark :help "(rectangle-exchange-point-and-mark &optional ARG)



Like `exchange-point-and-mark' but cycles through the rectangle's corners."] "--" ["Quit" keyboard-quit :help "(keyboard-quit)



Signal a `quit' condition.

During execution of Lisp code, this character causes a quit directly.

At top-level, as an editor command, this simply beeps."]))) (dolist (item '((begin-rect menu-item "--") ["Rectangle Mark Mode" rectangle-mark-mode :style toggle :selected rectangle-mark-mode :help "(rectangle-mark-mode &optional ARG)



Toggle the region as rectangular.

Activates the region if needed. Only lasts until the region is deactivated."] (after-rect menu-item "--"))) (easy-menu-add-item global-map '("menu-bar" "edit") item "bookmark"))

This snippet does the following

adds a menu entry for rectangle-mark-mode to Edit menu. adds a menu entry for tabify and untabify commands to Edit menu adds a menu named Rectangle which gets activated when in rectangle-mark-mode .

Step 2: Ensure that you have set up the above snippet correctly

Once you restart you Emacs, you should see menu entries mentioned in previous steps. See the GIF below for details. If you aren’t seeing what you see below, repeat the earlier steps.

[CLICK HERE FOR UNSCALED GIF]

[CLICK HERE FOR UNSCALED GIF]

Spend some time reviewing the above menus. The items there give a good overview of what the rectangle commands do.

Step 3: Copy the input table in to an Emacs buffer

Copy the input table either from this article, or from here (1). Note that the table scraped from this article will have spaces (and not tabs), but the one from the snippet will have tabs.

Step 4: Do the conversion

See the GIF below for details. The GIF not only shows how to solve the original problem, but also demonstrates the lesser known facet of rectangle commands: To indent and de-dent text.

[CLICK HERE FOR UNSCALED GIF]

[CLICK HERE FOR UNSCALED GIF]

Concluding Words

To many users, rectangle commands may seem like esoteric set of commands that are of little use in day-to-day editing. This was also my opinion when I learnt these commands a decade or so ago. Retrospectively, I couldn’t have been more wrong. In my experience, I use rectangle commands on a very regular basis. These commands once mastered, keeps on giving.

Appendix

Regular users of this blog will remember an earlier article which posed the same problem as this article, but suggested delim-col as a solution. (1) You may want to review that article, and compare and contrast the relative merits and de-merits of delim-col and rectangle based approaches.