6

I have a list in an org document that is rather long

- alpha a
- beta b
- gamma
- delta d
- epsilon e
- zeta z
- eta
- theta
- iota
- kappa
- lambda
- mu

I would like a function that redistributes this block of text into n columns where n is specified by me. So issuing M-x desired-function <ret> 2 <ret> would return something like

- alpha a    - eta
- beta b     - theta
- gamma      - iota
- delta d    - kappa
- epsilon e  - lambda
- zeta z     - mu

and issuing M-x desired-function <ret> 3 <ret> would return something like

- alpha a  - epsilon e  - iota
- beta b   - zeta z     - kappa
- gamma    - eta        - lambda
- delta d  - theta      - mu

Such a function would probably break the functionality of the list in the org document but that wouldn't really bother me.

Does this exist in emacs?

Brian Fitzpatrick
  • 2,265
  • 1
  • 17
  • 40
  • This is an interesting idea, but the problem is, this won't export as multiple columns in PDF or HTML. Though it might make sense to transform this into a table, would it? – wvxvw Aug 24 '15 at 07:11
  • @wvxvw Is there a way to easily get this list into an mxn table? – Brian Fitzpatrick Aug 24 '15 at 07:23

1 Answers1

3

Here's something quick I could think of:

(defun my/org-partition-list (n)
  (interactive "nColumns: ")
  (let* ((struct (org-list-struct))
         (prevs (org-list-prevs-alist struct))
         (start (org-list-get-list-begin (point-at-bol) struct prevs))
         (end (org-list-get-list-end (point-at-bol) struct prevs))
         (table 
          (orgtbl-to-orgtbl
           (cl-loop with chunk = nil
                    for item in
                    (cl-loop for item in struct
                             collect (org-trim
                                      (buffer-substring-no-properties
                                       (+ (cl-first item)
                                          (cl-second item)
                                          (length (cl-third item)))
                                       (car (last item)))))
                    for i from 1
                    if (zerop (mod i n))
                    collect (nreverse (cons item chunk)) into result
                    and do (setf chunk nil)
                    else do (push item chunk) end
                    finally (cl-return
                             (if (/= 0 (mod i n))
                                 (append result (list chunk))
                               result)))
           nil)))
    (delete-region start end)
    (insert table)))

One thing to keep in mind is that Org lists can contain many other Org elements, most of which will make this function output something unpredictable / not really usable. This will only work in trivial cases, when lists contain plain text, one line per entry.

wvxvw
  • 11,222
  • 2
  • 30
  • 55
  • Is there a version of this code that doesn't use org-* anything, doesn't use common lisp functions (cl-loop, collect, etc), and that just works with a simple list of strings as input? I looked around in emacs in the guts of `define-function`, but looks like completing-read (in C code) actually does the multi-column printing when I type "describe-function table- (and then type two TABs). – Kevin May 22 '16 at 02:36
  • @Kevin I cannot think of one right away, but I'd also look into `insert-rectangle` from `rect.el` - it seems to be relatively simple. Perhaps with little effort you will be able to adapt it to do what you need (you'd need to compute the longest line in the column on the left and add enough space so that insertion doesn't happen on top of existing text). – wvxvw May 22 '16 at 05:39
  • This posting taught me about align-regexp, and for that I am grateful. I ended up writing my own elisp function to do the alignments. See my posting here: (http://emacs.stackexchange.com/questions/22426/function-to-reshape-a-1-column-buffer-list-into-n-columns-with-align-regexp/22427#22427) – Kevin May 22 '16 at 15:32