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.

nispio
  • 8,175
  • 2
  • 35
  • 73
  • You could do your column-major reshaping, followed by transposing the table via org-mode commands. Here is an example from Mickey Petersen: http://www.masteringemacs.org/article/fun-vimgolf-4-transpositioning-text-tables – 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

4 Answers4

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.

Malabarba
  • 22,878
  • 6
  • 78
  • 163
  • 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**](http://www.gnu.org/software/emacs/manual/html_node/emacs/Basic-Keyboard-Macro.html) says, "*An argument of zero repeats the macro indefinitely, until it gets an error or you type `C-g` (or, on MS-DOS, `C-`).*" – Drew May 06 '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

abo-abo
  • 13,943
  • 1
  • 29
  • 43
  • 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
  • @nanny, yeah, `butterfly` would probably work as well. – abo-abo Jan 13 '15 at 22:16
  • 1
    @abo-abo I know that's in jest, but it's not as convoluted as it sounds. The extra `RET`s are just to accept the defaults, and the `y` to "repeat [the alignment] throughout line" – nanny Jan 14 '15 at 14:13
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.

Kaushal Modi
  • 25,203
  • 3
  • 74
  • 179
  • 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
  • @Malabarba For me, it concatenates the line below to the current line without any space in-between. `join-line` is alias to `delete-indentation` in `simple.el` and in there the last portion of `delete-indentation` is a call to `(fixup-whitespace)`. – Kaushal Modi Jan 13 '15 at 21:36
  • @kaushalmodi For me, it concatenates the *current line* to the *line above*, with a space in between. This is on `emacs -Q`, version 25.1. – Malabarba Jan 13 '15 at 22:26
  • @Malabarba For what its worth, I get a space when I use `join-line`, unless I am in the scratch buffer. Not sure why. – nispio Jan 13 '15 at 23:54
  • @nispio Thanks. That might be it. I have tested my solution only in the scratch buffer. I haven't yet edebugged `join-line` for the space inconsistency. Will update my solution on getting a chance to look more into this. – Kaushal Modi Jan 14 '15 at 00:12
  • 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 00:39
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.

phils
  • 48,657
  • 3
  • 76
  • 115