6

I asked this question on stack overflow, but haven't gotten to solve the problem yet, then I thought I should rather post it here.

Is there a way to bulk-reverse the order of headings in Emacs org-mode? I'd like to change this kind of list (but much bigger, not just two or three items, thus "bulk"):

* personal computer
 * Windows
 * Mac
 * Linux
* StackExchange
 * stackoverflow
* countries
* people

to this:

* people
* countries
* StackExchange
 * stackoverflow
* personal computer
 * Windows
 * Mac
 * Linux

I just want to reverse the list so that the last heading with its subtrees would be the first heading of a new list, second-last would be the second heading of the new list, and so on.

  • I've copied your explaination from stackoverflow.com/questions/34803502/…. It makes your question much more clearer. Hope you don't mind. – Tobias Jan 15 '16 at 13:15
  • When you click C-c ^ on a list in Org, it will offer to sort it using different parameters. One parameter is [f]function. Try completing it to see what options are there, if none matches your criteria, you could write your own taking one of the suggested by the completion as an example. – wvxvw Jan 15 '16 at 13:34
3

The following interactive function does what you want. Nevertheless, Junacho's warning applies:

This code is very fragile. It assumes that the parsed buffer has the structure (org-data nil (headline...) (headline...) ...). Anything else will fail.

Put the following code into your initialization file, e.g., ~/.emacs, evaluate this buffer or re-start emacs, open your org-file and run M-x org-reverse-headers.

(defun org-reverse-headers ()
  "Reverse headers of current org file"
  (interactive)
  (let (str
    (content (nthcdr 2 (org-element-parse-buffer 'headline)))) ;; `org-element-parse-buffer' returns (ORG-DATA PROPERTIES CONTENT), CONTENT contains the headlines
    (setq content (nreverse content)) ;; reversal of sections
    (insert
     (with-output-to-string
       (dolist (header content)
     (princ (buffer-substring (plist-get (cadr header) :begin) (plist-get (cadr header) :end))))
       (delete-region (point-min) (point-max))))))
  • Thanks @Tobias for coming up with a more complete answer! – Juancho Jan 15 '16 at 23:18
  • @Tobias Wow, thanks! It works perfectly, and I didn't know of the functions such as nthcdr, org-element-parse-buffer, or nreverse at all. It solved my current problem, and I can call this function whenever I have this kind of problem again in the future. Hopefully, I'll look into those functions to learn elisp more. – stacko Jan 16 '16 at 7:06
6

The following code block will parse the whole org buffer, reverse the first level sequence (hopefully all the level 1 headings), and then regenerate the org buffer again:

#+begin_src elisp :results raw
(org-element-interpret-data (cons 'org-data (cons nil (reverse (cddr (org-element-parse-buffer))))))
#+end_src

So, you can add this source block at the end of your document, press C-c C-c, and get the reversed document as a result.

Warnings

  1. This code is very fragile. It assumes that the parsed buffer has the structure (org-data nil (headline...) (headline...) ...). Anything else will fail.
  2. The code parses the whole buffer, so you should remove previous results before re-evaluating, lest you want an exponentially growing document.
  • 1
    Your method looks elegant. I've placed your code segment behind the text from the OP and executed the code block. In emacs 25.0.50.1 with org-mode 8.2.10 the list with '*'-bullets is transformed into a list with minus-signs as bullets. In my solution I copy the text of headings+body verbatim such that it does not change. Furthermore, I also delete the old text. Nevertheless, I upvote your solution because of the level of elegance. I believe my solution is a bit more roboust because it does not rely on the parsing of the deeper elements -- just the headings. – Tobias Jan 15 '16 at 16:29
  • @Juancho Thanks, it worked as well on my environment (desktop Emacs 24.4.9.0)! The star bullets didn't change, but it copied the code block and somehow inserted itself between the first and the second headings in the result list. It's not a big deal, but is this code supposed to work this way? – stacko Jan 16 '16 at 7:46
  • I had never fiddled with src blocks, and I've changed the keybindings of C-c C-c, so I had to look into the commands here: orgmode.org/manual/… And it took me a while to realize that I had to select code block first to run org-babel-execute-src-block successfully, or that I can alternatively call org-babel-execute-buffer. Hope this additional info I wrote here helps some beginners like me who see this page and have the same problem. – stacko Jan 16 '16 at 7:51

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.