From mboxrd@z Thu Jan 1 00:00:00 1970 From: Carsten Dominik Subject: Re: How to set a set of headlines (at the same level) Date: Thu, 16 Nov 2006 13:12:09 +0100 Message-ID: References: <200611160107.kAG176x0000353@mail01.syd.optusnet.com.au> Mime-Version: 1.0 (Apple Message framework v624) Content-Type: text/plain; charset=US-ASCII; format=flowed Content-Transfer-Encoding: 7bit Return-path: Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1Gkg6M-0007mR-Bt for emacs-orgmode@gnu.org; Thu, 16 Nov 2006 07:12:18 -0500 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1Gkg6I-0007iC-O7 for emacs-orgmode@gnu.org; Thu, 16 Nov 2006 07:12:17 -0500 Received: from [199.232.76.173] (helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Gkg6I-0007i2-Hi for emacs-orgmode@gnu.org; Thu, 16 Nov 2006 07:12:14 -0500 Received: from [146.50.4.51] (helo=imap.science.uva.nl) by monty-python.gnu.org with esmtp (Exim 4.52) id 1Gkg6I-00022W-Mh for emacs-orgmode@gnu.org; Thu, 16 Nov 2006 07:12:14 -0500 In-Reply-To: <200611160107.kAG176x0000353@mail01.syd.optusnet.com.au> List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org Errors-To: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org To: charles_cave@optusnet.com.au Cc: emacs-orgmode@gnu.org 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. > > Charles 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) (progn ;; 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) (show-subtree)) (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 (mapcar (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)" nentries (if unique (format ", %d duplicates removed" nremoved) ""))))