Function: org-sort-entries
org-sort-entries is an interactive and byte-compiled function defined
in org.el.gz.
Signature
(org-sort-entries &optional WITH-CASE SORTING-TYPE GETKEY-FUNC COMPARE-FUNC PROPERTY INTERACTIVE?)
Documentation
Sort entries on a certain level of an outline tree.
If there is an active region, the entries in the region are sorted. Else, if the cursor is before the first entry, sort the top-level items. Else, the children of the entry at point are sorted.
Sorting can be alphabetically, numerically, by date/time as given by a time stamp, by a property, by priority order, or by a custom function.
The command prompts for the sorting type unless it has been given to the
function through the SORTING-TYPE argument, which needs to be a character,
(?n ?N ?a ?A ?t ?T ?s ?S ?d ?D ?p ?P ?o ?O ?r ?R ?f ?F ?k ?K). Here is
the precise meaning of each character:
a Alphabetically, ignoring the TODO keyword and the priority, if any.
c By creation time, which is assumed to be the first inactive time stamp
at the beginning of a line.
d By deadline date/time.
k By clocking time.
n Numerically, by converting the beginning of the entry/item to a number.
o By order of TODO keywords.
p By priority according to the cookie.
r By the value of a property.
s By scheduled date/time.
t By date/time, either the first active time stamp in the entry, or, if
none exist, by the first inactive one.
Capital letters will reverse the sort order.
If the SORTING-TYPE is ?f or ?F, then GETKEY-FUNC specifies a function to be called with point at the beginning of the record. It must return a value that is compatible with COMPARE-FUNC, the function used to compare entries.
Comparing entries ignores case by default. However, with an optional argument WITH-CASE, the sorting considers case as well.
Sorting is done against the visible part of the headlines, it ignores hidden links.
When sorting is done, call org-after-sorting-entries-or-items-hook.
A non-nil value for INTERACTIVE? is used to signal that this function is being called interactively.
Key Bindings
Source Code
;; Defined in /usr/src/emacs/lisp/org/org.el.gz
(defun org-sort-entries
(&optional with-case sorting-type getkey-func compare-func property
interactive?)
"Sort entries on a certain level of an outline tree.
If there is an active region, the entries in the region are sorted.
Else, if the cursor is before the first entry, sort the top-level items.
Else, the children of the entry at point are sorted.
Sorting can be alphabetically, numerically, by date/time as given by
a time stamp, by a property, by priority order, or by a custom function.
The command prompts for the sorting type unless it has been given to the
function through the SORTING-TYPE argument, which needs to be a character,
\(?n ?N ?a ?A ?t ?T ?s ?S ?d ?D ?p ?P ?o ?O ?r ?R ?f ?F ?k ?K). Here is
the precise meaning of each character:
a Alphabetically, ignoring the TODO keyword and the priority, if any.
c By creation time, which is assumed to be the first inactive time stamp
at the beginning of a line.
d By deadline date/time.
k By clocking time.
n Numerically, by converting the beginning of the entry/item to a number.
o By order of TODO keywords.
p By priority according to the cookie.
r By the value of a property.
s By scheduled date/time.
t By date/time, either the first active time stamp in the entry, or, if
none exist, by the first inactive one.
Capital letters will reverse the sort order.
If the SORTING-TYPE is ?f or ?F, then GETKEY-FUNC specifies a function to be
called with point at the beginning of the record. It must return a
value that is compatible with COMPARE-FUNC, the function used to
compare entries.
Comparing entries ignores case by default. However, with an optional argument
WITH-CASE, the sorting considers case as well.
Sorting is done against the visible part of the headlines, it ignores hidden
links.
When sorting is done, call `org-after-sorting-entries-or-items-hook'.
A non-nil value for INTERACTIVE? is used to signal that this
function is being called interactively."
(interactive (list current-prefix-arg nil nil nil nil t))
(let ((case-func (if with-case 'identity 'downcase))
start beg end stars re re2
txt what tmp)
;; Find beginning and end of region to sort
(cond
((org-region-active-p)
(setq start (region-beginning)
end (region-end))
;; we will sort the region
;; Limit the region to full headings.
(goto-char start)
;; Move to beginning of heading.
;; If we are inside heading, move to next.
;; If we are on heading, move to its begin position.
(if (org-at-heading-p)
(forward-line 0)
(outline-next-heading))
(setq start (point))
;; Extend region end beyond the last subtree.
(goto-char end)
(org-end-of-subtree nil t)
(setq end (point)
what "region")
(goto-char start))
((or (org-at-heading-p)
(ignore-errors (progn (org-back-to-heading) t)))
;; we will sort the children of the current headline
(org-back-to-heading)
(setq start (point)
end (progn (org-end-of-subtree t t)
(or (bolp) (insert "\n"))
(when (>= (org-back-over-empty-lines) 1)
(forward-line 1))
(point))
what "children")
(goto-char start)
(org-fold-show-subtree)
(outline-next-heading))
(t
;; we will sort the top-level entries in this file
(goto-char (point-min))
(or (org-at-heading-p) (outline-next-heading))
(setq start (point))
(goto-char (point-max))
(forward-line 0)
(when (looking-at ".*?\\S-")
;; File ends in a non-white line
(end-of-line 1)
(insert "\n"))
(setq end (point-max))
(setq what "top-level")
(goto-char start)
(org-fold-show-all '(headings drawers blocks))))
(setq beg (point))
(when (>= beg end) (goto-char start) (user-error "Nothing to sort"))
(looking-at "\\(\\*+\\)")
(setq stars (match-string 1)
re (concat "^" (regexp-quote stars) " +")
re2 (concat "^" (regexp-quote (substring stars 0 -1)) "[ \t\n]")
txt (buffer-substring beg end))
(unless (equal (substring txt -1) "\n") (setq txt (concat txt "\n")))
(when (and (not (equal stars "*")) (string-match re2 txt))
(user-error "Region to sort contains a level above the first entry"))
(unless sorting-type
(message
"Sort %s: [a]lpha [n]umeric [p]riority p[r]operty todo[o]rder [f]unc
[t]ime [s]cheduled [d]eadline [c]reated cloc[k]ing
A/N/P/R/O/F/T/S/D/C/K means reversed:"
what)
(setq sorting-type (read-char-exclusive)))
(unless getkey-func
(and (= (downcase sorting-type) ?f)
(setq getkey-func
(or (and interactive?
(org-read-function
"Function for extracting keys: "))
(error "Missing key extractor")))))
(and (= (downcase sorting-type) ?r)
(not property)
(setq property
(completing-read "Property: "
(mapcar #'list (org-buffer-property-keys t))
nil t)))
(when (member sorting-type '(?k ?K)) (org-clock-sum))
(message "Sorting entries...")
(save-restriction
(narrow-to-region start end)
;; No trailing newline - add one to avoid
;; * heading
;; text* another heading
(save-excursion
(goto-char end)
(unless (bolp) (insert "\n")))
(let ((restore-clock?
;; The clock marker is lost when using `sort-subr'; mark
;; the clock with temporary `:org-clock-marker-backup'
;; text property.
(when (and (eq (org-clocking-buffer) (current-buffer))
(<= start (marker-position org-clock-marker))
(>= end (marker-position org-clock-marker)))
(with-silent-modifications
(put-text-property (1- org-clock-marker) org-clock-marker
:org-clock-marker-backup t))
t))
(dcst (downcase sorting-type))
(case-fold-search nil)
(now (current-time)))
(org-preserve-local-variables
(sort-subr
(/= dcst sorting-type)
;; This function moves to the beginning character of the
;; "record" to be sorted.
(lambda nil
(if (re-search-forward re nil t)
(goto-char (match-beginning 0))
(goto-char (point-max))))
;; This function moves to the last character of the "record" being
;; sorted.
(lambda nil
(save-match-data
(condition-case nil
(outline-forward-same-level 1)
(error
(goto-char (point-max))))))
;; This function returns the value that gets sorted against.
(lambda ()
(cond
((= dcst ?n)
(string-to-number
(org-sort-remove-invisible (org-get-heading t t t t))))
((= dcst ?a)
(funcall case-func
(org-sort-remove-invisible (org-get-heading t t t t))))
((= dcst ?k)
(or (get-text-property (point) :org-clock-minutes) 0))
((= dcst ?t)
(let ((end (save-excursion (outline-next-heading) (point))))
(if (or (re-search-forward org-ts-regexp end t)
(re-search-forward org-ts-regexp-both end t))
(org-time-string-to-seconds (match-string 0))
(float-time now))))
((= dcst ?c)
(let ((end (save-excursion (outline-next-heading) (point))))
(if (re-search-forward
(concat "^[ \t]*\\[" org-ts-regexp1 "\\]")
end t)
(org-time-string-to-seconds (match-string 0))
(float-time now))))
((= dcst ?s)
(let ((end (save-excursion (outline-next-heading) (point))))
(if (re-search-forward org-scheduled-time-regexp end t)
(org-time-string-to-seconds (match-string 1))
(float-time now))))
((= dcst ?d)
(let ((end (save-excursion (outline-next-heading) (point))))
(if (re-search-forward org-deadline-time-regexp end t)
(org-time-string-to-seconds (match-string 1))
(float-time now))))
((= dcst ?p)
(if (re-search-forward org-priority-regexp (line-end-position) t)
(string-to-char (match-string 2))
org-priority-default))
((= dcst ?r)
(or (org-entry-get nil property) ""))
((= dcst ?o)
(when (looking-at org-complex-heading-regexp)
(let* ((m (match-string 2))
(s (if (member m org-done-keywords) '- '+)))
(- 99 (funcall s (length (member m org-todo-keywords-1)))))))
((= dcst ?f)
(if getkey-func
(progn
(setq tmp (funcall getkey-func))
(when (stringp tmp) (setq tmp (funcall case-func tmp)))
tmp)
(error "Invalid key function `%s'" getkey-func)))
(t (error "Invalid sorting type `%c'" sorting-type))))
nil
(cond
((= dcst ?a) #'org-string<)
((= dcst ?f)
(or compare-func
(and interactive?
(org-read-function
(concat "Function for comparing keys "
"(empty for default `sort-subr' predicate): ")
'allow-empty))))
((member dcst '(?p ?t ?s ?d ?c ?k)) '<))))
(org-cycle-hide-drawers 'all)
(when restore-clock?
(move-marker org-clock-marker
(1+ (next-single-property-change
start :org-clock-marker-backup)))
(remove-text-properties (1- org-clock-marker) org-clock-marker
'(:org-clock-marker-backup t)))))
(run-hooks 'org-after-sorting-entries-or-items-hook)
(message "Sorting entries...done")))