M-x all-things-emacs

Quick Tip: set-goal-column

March 17th, 2007 by Ryan McGeary · 7 Comments

On the surface, it might not sound like editing columns of text is something that happens a lot, but I find myself doing it fairly often. A quick way to enter different values on each line down a column is to use set-goal-column (bound to C-x C-n).

Let’s say we have a block of text like the following comma-separated list. We’d like to enter a different value in the second field of each line. First we position the point on the first line between the two commas. This is at column position 6 (emacs starts counting columns at zero).

Step 1: set-goal-column

Now let’s input our first text field and hit C-n to go to the next line.

Step 2: set-goal-column

It does what you might expect. Because our first field pushed us out to column position 23, when we went to the next line our point was placed at the end of the second line. To enter a new field on line 2, we’ll have to first jump back to column 6 manually. That’s no fun. Let’s start over and use set-goal-column instead.

Step 1: set-goal-column

Now, before we enter our first field, we’ll first hit C-x C-n. Setting a goal column sets the current horizontal position as a goal for C-n and C-p. Let’s see what happens when we re-input our first field and hit C-n.

Step 3: set-goal-column

Perfect. We’re right were we need to be. Quickly, we finish our text entry down the column:

Step 4: set-goal-column

To turn off the goal column, pass a non-nil argument to set-goal-column (i.e. C-u C-x C-n).

Note: When you run set-goal-column for the first time, you’ll receive a warning that the function is disabled. You can either follow the built-in instructions and customizations to enable the function, or you can add the following to your emacs initialization.

(put 'set-goal-column 'disabled nil)

This exercise provided a nice way to manage a column of text with different values on each line. When the task is more repetitive, emacs rectangles come in handy. We’ll make rectangles a future topic of discussion.

Tags: quick · tips

7 responses so far ↓

  • 1 Jeff Squires // Mar 22, 2007 at 5:46 pm

    Thanks Ryan.

    Like so many aspects of emacs, I never knew this existed until I read your post.

    I usually just compose the text I need to insert, then copy it to a rectangle.

    But, I’m going to start using this now. I wonder why it is disabled by default.

    BTW, slightly on-topic: Does anything know why there isn’t a way (or maybe there is) to copy a rectangle instead of killing it as well. I am aware of the function: copy-rectangle-to-register, but I must admit I can’t figure out how to paste it from the register (and it seems a bit clunky that I have to use a register).

  • 2 Ryan McGeary // Mar 22, 2007 at 7:27 pm

    Jeff, Good question. I think I’ve actually ingrained myself to kill a rectangle and then immediately undo it back to simulate a copy (the rectangle stays as the “last killed” even after the undo).

    It sounds like what you’re asking for is a kill-save-rectangle function to compliment kill-rectangle (just like kill-ring-save compliments kill-region). Since your gripe is my gripe, I took a quick stab at writing such a function:

    1
    2
    3
    4
    5
    6
    7
    
    (defun kill-save-rectangle (start end &optional fill)
      "Save the rectangle as if killed, but don't kill it.  See
    `kill-rectangle' for more information."
      (interactive "r\nP")
      (kill-rectangle start end fill)
      (goto-char start)
      (yank-rectangle))

    This might benefit from some save-excursion action, but it seems to work better for me without. I bound it to C-x r M-k to compliment C-x r k (just like M-w compliments C-w):

    (global-set-key (kbd "C-x r M-k") 'kill-save-rectangle)
  • 3 blalor // Mar 23, 2007 at 5:28 pm

    TextMate has a nifty feature that allows you to type in a column, but with the start of the region relative to the end of the line. For example, consider the following text:

    asdf
    asdffdsa
    asdfqwerty

    You’d select all the lines above, hit a key sequence (which escapes me at the moment) and when you type, the characters all appear at the end of the line. If you moved the cursor left two characters before typing, the text would appear two characters in from the end of the line.

    Is this possible with Emacs? Scratch that, I know it’s possible. How do I do it? :-)

  • 4 Ryan McGeary // Mar 23, 2007 at 6:36 pm

    blalor, Good question. I’m familiar with that TextMate feature:

    ⌥⌘A (Text -> Edit Each Line in Selection) lets you insert text at the end of each line in the current selection.

    I normally perform a quick keyboard macro to accomplish the same task in emacs. The macro typically starts with C-e and ends with C-n, but I must admit, the TextMate feature is more elegant than that for the simpler cases. For the more advanced cases where you might need to insert text before the last word on each line, a keyboard macro (in either editor) probably works best.

    Another alternative is to use query-replace-regexp with transient-mark-mode on. Highlight the region, run query-replace-regexp (C-M-%), and substitute $ (end of line) with the text that you want to append. Hit ! to replace all remaining matches within the region.

    With a bit of time, I’m sure someone could write a more elegant function to simulate the TextMate behavior, but I’m not aware if something is already available.

  • 5 blalor // Mar 24, 2007 at 11:44 am

    Thanks for that, Ryan. I’d forgotten about the regexp way. I figured a macro would be the way to go. Have to get cracking on that.

  • 6 Keyboard Macros in the Wild: The Mundane SQL Fix | M-x all-things-emacs // Apr 5, 2007 at 8:32 pm

    [...] solution was to move point to the start of the unique id 248 in the first row and to set a goal column (C-x C-n) at column 65 (this location). I then created the following macro by calling M-5 M-0 M-0 [...]

  • 7 hrbg // Dec 5, 2010 at 1:27 am

    great post, so that’s C-x C-n was for!!. Many times I typed this by mistake now I see how useful this is

    @Jeff Squires

    you can copy a rectangle of text using cua-mode selecting the text and then use M-w to copy