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.

  • 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 Jan 13 '15 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 Jan 13 '15 at 20:57
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.

  • Do I have to require an extra package to use pull-up-line? It doesn't seem to exist in my current environment. – nispio Jan 13 '15 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)) – Kaushal Modi Jan 13 '15 at 20:56
  • @nispio I have replaced M-x pull-up-line with C-u M-x join-line (same thing). – Kaushal Modi Jan 13 '15 at 21:02
  • Do you need to manually enter that space? Doesn't join-line do that already? – Malabarba Jan 13 '15 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. – Kaushal Modi Jan 14 '15 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.

  • 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). – Amelio Vazquez-Reina Apr 21 '15 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 May 6 '15 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

  • 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 Jan 13 '15 at 18:42
  • align-cols is not included with emacs – Jordon Biondo Jan 13 '15 at 18:43
  • whoops, you're right, I'll add the code. – abo-abo Jan 13 '15 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 Jan 13 '15 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 Jan 14 '15 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, privacy policy and cookie policy

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