3

I am looking for an easy way to turn a list such as this:

one,
two,
three,
four,
five,
...

into something more like this:

one,      two,      three,   four,
five,     six,      seven,   eight,
nine,     ten,      eleven,  twelve,
thirteen, fourteen, fifteen, sixteen,

It seems relatively straight-forward to do "column-major" reshaping by using rectangles, but more often than not it is the "row-major" reshaping that I want, since many programming languages accept a list in this format.

I am a big fan of the multiple-cursors package, so I would gladly accept an answer which presents a clever usage of mc features. That is not a requirement though. I find that multiple-cursors can get a bit laggy after >100 cursors, so there may be better ways to reshape a list when the list is long anyway.

2
  • You could do your column-major reshaping, followed by transposing the table via org-mode commands. Here is an example from Mickey Petersen: masteringemacs.org/article/…
    – deprecated
    Commented Jan 13, 2015 at 18:28
  • @deprecated I had a similar idea, but if I have 100 entries which I want to reshape as 25 rows by 4 columns, I would have to first kill and yank 25 rectangles, which seems almost as inconvenient as doing the reshape manually.
    – nispio
    Commented Jan 13, 2015 at 20:57

4 Answers 4

1

Solution

This solution is derived from the below discussion. The only difference is to not manually insert spaces and use an alternative regex for alignment with does not need spaces and also works with text that has spaces in it.

  1. Re-ordering: M-< F3 C-u M-^ C-x z z C-n F4 M-0 F4
  2. Select whole buffer: C-x h
  3. Align
    • C-u M-x align-regexp
    • Replace the content in the minibuffer with ,\(\) RET
    • RET
    • RET
    • y

Result

one,       two,       three,    four,    
five,      six,       seven,    eight,   
nine,      ten,       eleven,   twelve,  
thirteen,  fourteen,  fifteen,  sixteen,  

Discussion

  1. M-x beginning-of-buffer
  2. M-x start-kbd-macro -- Start recording macro
  3. Do below 3 times
    • C-u M-x join-line (I have this bound to C-j for my convenience)
    • Hit SPACE -- Enter a space character which will later help with alignment. This is required only in emacs-elisp-mode. But even if this space is added regardless, this solution will still work.
  4. M-x next-line (C-n)
  5. M-x kmacro-end-or-call-macro -- Stop recording macro
  6. M-0 C-x e -- Run the macro till the end of buffer is reached. (ref)
  7. M-x mark-whole-buffer (C-x h)
  8. C-u M-x align-regexp -> SPACE RET, RET, RET, y -- Do the regex align by Space character for all columns. Note that if you use , instead of SPACE, you can align content having the space characters too.

Explanation about when it is required to add that extra space in Step 3 above

The function fixup-whitespace called in delete-indentation function (which join-line aliases to) is responsible for adding that space automatically for major-modes not equal to emacs-lisp-mode.

The reason is the following line in fixup-whitespace function (in simple.el):

(looking-at "$\\|\\s(\\|\\s'"))`

If this expression is true, space is NOT inserted after finishing the join operation.

That expression is true when any one of these is true:

  • if the point is at the end of line
  • if the current character is an open parenthesis char as defined in the major-mode's syntax table
  • if the current character is an expression prefix as defined in the major-mode's syntax table

And ... ', , and # are counted as expression prefixes in emacs-lisp-mode. At the end of joining operation, the point is on the , character at the end of the line (e.g. one, or two, or three,) and so the above looking-at expression becomes true when the major mode is emacs-lisp-mode; and thus the need to add that extra space manually in Step 3 for this mode.

Of course you don't have to worry about the space not being auto inserted if alignment is done at comma instead of space character in Step 8.

9
  • Do I have to require an extra package to use pull-up-line? It doesn't seem to exist in my current environment.
    – nispio
    Commented Jan 13, 2015 at 20:33
  • Ah, missed that, it's simply (defun pull-up-line () "Join the following line onto the current one (analogous to 'C-e', 'C-d')" (interactive) (join-line -1)) Commented Jan 13, 2015 at 20:56
  • @nispio I have replaced M-x pull-up-line with C-u M-x join-line (same thing). Commented Jan 13, 2015 at 21:02
  • Do you need to manually enter that space? Doesn't join-line do that already?
    – Malabarba
    Commented Jan 13, 2015 at 21:30
  • 1
    @nispio @Malabarba It's the emacs-lisp-mode in which we need the extra space. I happened to have my scratch buffer in that mode. If I change the major mode to org-mode or fundamental-mode or even an arbitrary mode like verilog-mode (does not come with emacs), I do NOT need to insert that extra space. So YMMV with the major mode. But even if we are adding an extra space, the align-regexp step stays the same and always works. Commented Jan 14, 2015 at 0:39
3

I too love multiple cursors, but for this one I'd use keyboard macros.

F3 C-3 C-n M-^ M-^ M-^ C-n C-0 F4

Which means:

  1. Start kbd macro.
  2. Move down 3 lines.
  3. Join 3 lines up.
  4. Move to next line.
  5. Finish and repeat kbd macro indefinitely.

You can then proceed to use align-regexp like the other answers explain.

2
  • This looks great Malabarba. What command is exactly issued with F4? I see end-kbc-macro, but you said "Finish and repeat kbd macro indefinitely" (which I presume is F4). Commented Apr 21, 2015 at 18:24
  • 2
    @AmelioVazquez-Reina: It is the 0 prefix arg passed to f4 that means execute the macro repeatedly (forever). As the doc says, "An argument of zero repeats the macro indefinitely, until it gets an error or you type C-g (or, on MS-DOS, C-<BREAK>)."
    – Drew
    Commented May 6, 2015 at 16:21
2
  1. M-x set-fill-column as desired.
  2. M-q: fill-paragraph.
  3. M-x align-cols.

The code for align-cols is here

6
  • 1
    Ingenious! However, thirteen, fourteen, fifteen, is longer than one, two, three, four, so it won't work for the exact example given. But hopefully the real use case has words with a more uniform length.
    – deprecated
    Commented Jan 13, 2015 at 18:42
  • align-cols is not included with emacs Commented Jan 13, 2015 at 18:43
  • whoops, you're right, I'll add the code.
    – abo-abo
    Commented Jan 13, 2015 at 18:53
  • Instead of align-cols, you can use the align-regexp from @kaushalmodi's answer: C-u M-x align-regexp SPC RET RET RET y.
    – nanny
    Commented Jan 13, 2015 at 22:00
  • 1
    @abo-abo I know that's in jest, but it's not as convoluted as it sounds. The extra RETs are just to accept the defaults, and the y to "repeat [the alignment] throughout line"
    – nanny
    Commented Jan 14, 2015 at 14:13
1
  • Mark the region
  • C-uM-| column -x RET

or column -x -c N for lines of (up to) N characters in length.

column is a BSD-derived shell command, which you quite likely have if you're on a Unix-like OS.

The -x argument gives the "row-major" output that you wanted.

For example, column -x -c 70 gives you the exact number of rows and columns that you showed in the question:

one,        two,        three,      four,
five,       six,        seven,      eight,
nine,       ten,        eleven,     twelve,
thirteen,   fourteen,   fifteen,    sixteen,

I don't see a way to specify the minimum spacing between columns, but the main problem with this is that the tab width used by column might not match your tab-width in Emacs, which might give you some wonky-looking output (even though it's formatted nicely for some other value).

However, once you have the basic arrangement of rows and columns, you can use align-regexp to tidy up if necessary:

C-uM-x align-regexp

The pattern would be: ,\(\s-*\) (i.e. prefix a comma to the default pattern). The next two options are default, and then say "Yes" to "Repeat throughout the line".

n.b. I believe you're going to run into trouble with some of the other answers as soon as your data contains spaces, which is why I think using a tool (column) already designed for this task is sensible, and why I recommend using the commas for alignment if you use align-regexp, rather than spaces.

Note that when indent-tabs-mode is non-nil, align-regexp will insert tabs for alignment. You can either untabify to convert them to spaces, or use https://stackoverflow.com/a/8129994/324105 to prevent it from happening.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.