[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Orgmode] How to set a set of headlines (at the same level)

From: Carsten Dominik
Subject: Re: [Orgmode] How to set a set of headlines (at the same level)
Date: Thu, 16 Nov 2006 13:12:09 +0100

On Nov 16, 2006, at 2:07, Charles Cave wrote:

I would like to see a feature to sort the set of branches into alphabetical order.
At the moment I have to manually move the subtrees up and done.


The sorting algorithm needs to call  org-move-subtree-up  and
org-move-subtree-down depending on the comparison of adjacent headings.

How difficult would it be to add this functionality? The command should
work on a region.  I want to create an org-mode file of vocabulary.
I note that it is possible to sort table rows so that is a possible
work around for me.


Sorting could indeed be done with a bubble algorithm or something
like that, using org-move-subtree commands to swap entries.
However, for long lists this would become very slow indeed, because you
would all the time search for the next headline and swap entries, both
slow things.

It is much better to build a list that then can be sorted
efficiently by the internal Emacs command `sort'.

I quickly hacked the following function which should do the job,
but I have not extensively tested it.  Maybe you could do the testing,
and I could included an improved version later with org.el.

(defun org-sort-entries (&optional case-sensitively)
  "Sort entries on a certain level of an outline tree alphabetically.
If there is an active region, the entries in the region are sorted.
If not, the children of the entry at point are sorted.

Comparing entries ignores case by default. However, with an optional argument CASE-SENSITIVELY, the sorting considers case as well. With two prefix arguments `C-u C-u', sorting is case-sensitive and duplicate entries will be removed."
  (interactive "P")
  (let ((unique (equal case-sensitively '(16)))
        (casefun (if case-sensitively 'identity 'downcase))
        start beg end entries stars re re2 p nentries (nremoved 0) last txt)
    ;; Find beginning and end of region to sort
    (if (org-region-active-p)
          ;; we will sort the region
          (setq end (region-end))
          (goto-char (1- (setq start (region-beginning)))))
      ;; we will sort the children of the current headline
      (setq start (point) end (org-end-of-subtree))
      (goto-char start)
    (outline-next-heading) ; this is the first heading to be included
    (setq beg (point))
    (if (>= (point) end) (error "Nothing to sort"))
    (looking-at "\\(\\*+\\)")
    (setq stars (match-string 1)
          re (concat "^" (regexp-quote stars) " +")
          re2 (concat "^" (regexp-quote (substring stars 0 -1)) "[^*]")
          txt (buffer-substring beg end))
    (if (string-match re2 txt)
        (error "Region to sort contains a level above the first entry"))
    ;; Make a list that can be sorted.
    ;; The car is the string for comparison, the cdr is the subtree
    (message "Sorting entries...")
    (setq entries
           (lambda (x)
             (string-match "^.*" x)
             (cons (funcall casefun (match-string 0 x)) x))
           (org-split-string txt re)))
    ;; Sort the list
(setq entries (sort entries (lambda (a b) (string< (car a) (car b)))))
    ;; Remove the old stuff
    (goto-char beg)
    (kill-region beg end)
    (setq nentries (length entries))
;; Insert the sorted entries, and remove duplicate if this is required
    (while (setq p (pop entries))
      (if (and unique (equal last (setq last (org-trim (cdr p)))))
          (setq nremoved (1+ nremoved)) ; same entry as before, skip it
        (insert stars " " (cdr p))))
    (goto-char start)
    (message "Sorting entries...done (%d entries%s)"
             (if unique (format ", %d duplicates removed" nremoved) ""))))

reply via email to

[Prev in Thread] Current Thread [Next in Thread]