Кто-нибудь знает о функции / модификации org-sort
которая может перекомпилировать / организовать группу TODO так, чтобы она сортировала их по трем (3) критериям: первая сортировка по сроку, вторая сортировка по приоритету и третья сортировка по названию задание?
РЕДАКТИРОВАТЬ: Я полагаю, что org-sort
к крайнему сроку (?d
) имеет ошибку, которая не может правильно обрабатывать датированные задачи. Я работаю над обходным путем (т. Е. Перенести недатированный todo в другой заголовок до истечения срока (?d
) происходит сортировка), но, возможно, лучше всего попытаться исправить исходную функцию сортировки. Обходной путь можно найти в следующем потоке (т. Е. Перенести недатированные задачи в другой заголовок одним махом): Как автоматизировать org-refile для нескольких задач
РЕДАКТИРОВАТЬ: Следующий код выглядит многообещающе, но требует некоторого TLC и обходного пути для недатированной ошибки todo:
;; multiple sort
(defun org-sort-multi (&rest sort-types)
"Multiple sorts on a certain level of an outline tree, or plain list items.
SORT-TYPES is a list where each entry is either a character or a
cons pair (BOOL . CHAR), where BOOL is whether or not to sort
case-sensitively, and CHAR is one of the characters defined in
`org-sort-entries-or-items'. Entries are applied in back to
front order.
Example: To sort first by TODO status, then by priority, then by
date, then alphabetically (case-sensitive) use the following
call:
(org-sort-multi '(?d ?p ?t (t . ?a)))"
(interactive)
(dolist (x (nreverse sort-types))
(when (char-valid-p x)
(setq x (cons nil x)))
(condition-case nil
(org-sort-entries (car x) (cdr x))
(error nil))))
;; sort current level
(defun lawlist-sort (&rest sort-types)
"Sort the current org level.
SORT-TYPES is a list where each entry is either a character or a
cons pair (BOOL . CHAR), where BOOL is whether or not to sort
case-sensitively, and CHAR is one of the characters defined in
`org-sort-entries-or-items'. Entries are applied in back to
front order.
Defaults to \"?o ?p\" which is sorted by TODO status, then by
priority"
(interactive)
(when (equal mode-name "Org")
(let ((sort-types (or sort-types
(if (or (org-entry-get nil "TODO")
(org-entry-get nil "PRIORITY"))
'(?d ?t ?p) ;; date, time, priority
'((nil . ?a))))))
(save-excursion
(outline-up-heading 1)
(let ((start (point))
end)
(while (and (not (bobp)) (not (eobp)) (<= (point) start))
(condition-case nil
(outline-forward-same-level 1)
(error (outline-up-heading 1))))
(unless (> (point) start)
(goto-char (point-max)))
(setq end (point))
(goto-char start)
(apply 'org-sort-multi sort-types)
(goto-char end)
(when (eobp)
(forward-line -1))
(when (looking-at "^\\s-*$")
(kill-whole-line)
)
(goto-char start)
;; (dotimes (x ) (org-cycle))
)))))
РЕДАКТИРОВАТЬ: Эта функция также показывает обещание, и я сообщу, когда я сделал больше тестирования.
(defun org-sort-all ()
(interactive)
(save-excursion
(goto-char (point-min))
(while (re-search-forward "^\* " nil t)
(goto-char (match-beginning 0))
(condition-case err
(progn
(org-sort-entries t ?a)
(org-sort-entries t ?p)
(org-sort-entries t ?o)
(forward-line))
(error nil)))
(goto-char (point-min))
(while (re-search-forward "\* PROJECT " nil t)
(goto-char (line-beginning-position))
(ignore-errors
(org-sort-entries t ?a)
(org-sort-entries t ?p)
(org-sort-entries t ?o))
(forward-line))))
РЕДАКТИРОВАТЬ: Для авантюрных душ, заинтересованных в установлении сроков сортировки (?d
) так что недатированный todo перемещен в нижнюю часть схемы, а не смешан с датированным todo, вот выдержка из текущего org.el
включенного в Emacs Trunk (по состоянию на 1 июля 2013 г.):
(defun org-sort (with-case)
"Call `org-sort-entries', `org-table-sort-lines' or `org-sort-list'.
Optional argument WITH-CASE means sort case-sensitively."
(interactive "P")
(cond
((org-at-table-p) (org-call-with-arg 'org-table-sort-lines with-case))
((org-at-item-p) (org-call-with-arg 'org-sort-list with-case))
(t
(org-call-with-arg 'org-sort-entries with-case))))
(defun org-sort-remove-invisible (s)
(remove-text-properties 0 (length s) org-rm-props s)
(while (string-match org-bracket-link-regexp s)
(setq s (replace-match (if (match-end 2)
(match-string 3 s)
(match-string 1 s)) t t s)))
s)
(defvar org-priority-regexp) ; defined later in the file
(defvar org-after-sorting-entries-or-items-hook nil
"Hook that is run after a bunch of entries or items have been sorted.
When children are sorted, the cursor is in the parent line when this
hook gets called. When a region or a plain list is sorted, the cursor
will be in the first entry of the sorted region/list.")
(defun org-sort-entries
(&optional with-case sorting-type getkey-func compare-func property)
"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 or by priority.
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). Here is the
precise meaning of each character:
n Numerically, by converting the beginning of the entry/item to a number.
a Alphabetically, ignoring the TODO keyword and the priority, if any.
o By order of TODO keywords.
t By date/time, either the first active time stamp in the entry, or, if
none exist, by the first inactive one.
s By the scheduled date/time.
d By deadline date/time.
c By creation time, which is assumed to be the first inactive time stamp
at the beginning of a line.
p By priority according to the cookie.
r By the value of a property.
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 either
a string or a number that should serve as the sorting key for that record.
Comparing entries ignores case by default. However, with an optional argument
WITH-CASE, the sorting considers case as well."
(interactive "P")
(let ((case-func (if with-case 'identity 'downcase))
(cmstr
;; The clock marker is lost when using `sort-subr', let's
;; store the clocking string.
(when (equal (marker-buffer org-clock-marker) (current-buffer))
(save-excursion
(goto-char org-clock-marker)
(looking-back "^.*") (match-string-no-properties 0))))
start beg end stars re re2
txt what tmp)
;; Find beginning and end of region to sort
(cond
((org-region-active-p)
;; we will sort the region
(setq end (region-end)
what "region")
(goto-char (region-beginning))
(if (not (org-at-heading-p)) (outline-next-heading))
(setq start (point)))
((or (org-at-heading-p)
(condition-case nil (progn (org-back-to-heading) t) (error nil)))
;; 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"))
(org-back-over-empty-lines)
(point))
what "children")
(goto-char start)
(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))
(beginning-of-line 1)
(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)
(show-all)))
(setq beg (point))
(if (>= beg 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)) "[ \t\n]")
txt (buffer-substring beg end))
(if (not (equal (substring txt -1) "\n")) (setq txt (concat txt "\n")))
(if (and (not (equal stars "*")) (string-match re2 txt))
(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
A/N/P/R/O/F/T/S/D/C means reversed:"
what)
(setq sorting-type (read-char-exclusive))
(and (= (downcase sorting-type) ?f)
(setq getkey-func
(org-icompleting-read "Sort using function: "
obarray 'fboundp t nil nil))
(setq getkey-func (intern getkey-func)))
(and (= (downcase sorting-type) ?r)
(setq property
(org-icompleting-read "Property: "
(mapcar 'list (org-buffer-property-keys t))
nil t))))
(message "Sorting entries...")
(save-restriction
(narrow-to-region start end)
(let ((dcst (downcase sorting-type))
(case-fold-search nil)
(now (current-time)))
(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 nil
(cond
((= dcst ?n)
(if (looking-at org-complex-heading-regexp)
(string-to-number (match-string 4))
nil))
((= dcst ?a)
(if (looking-at org-complex-heading-regexp)
(funcall case-func (match-string 4))
nil))
((= 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))
(org-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))
(org-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))
(org-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))
(org-float-time now))))
((= dcst ?p)
(if (re-search-forward org-priority-regexp (point-at-eol) t)
(string-to-char (match-string 2))
org-default-priority))
((= dcst ?r)
(or (org-entry-get nil property) ""))
((= dcst ?o)
(if (looking-at org-complex-heading-regexp)
(- 9999 (length (member (match-string 2)
org-todo-keywords-1)))))
((= dcst ?f)
(if getkey-func
(progn
(setq tmp (funcall getkey-func))
(if (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) 'string<)
((= dcst ?f) compare-func)
((member dcst '(?p ?t ?s ?d ?c)) '<)))))
(run-hooks 'org-after-sorting-entries-or-items-hook)
;; Reset the clock marker if needed
(when cmstr
(save-excursion
(goto-char start)
(search-forward cmstr nil t)
(move-marker org-clock-marker (point))))
(message "Sorting entries...done")))
(defun org-do-sort (table what &optional with-case sorting-type)
"Sort TABLE of WHAT according to SORTING-TYPE.
The user will be prompted for the SORTING-TYPE if the call to this
function does not specify it. WHAT is only for the prompt, to indicate
what is being sorted. The sorting key will be extracted from
the car of the elements of the table.
If WITH-CASE is non-nil, the sorting will be case-sensitive."
(unless sorting-type
(message
"Sort %s: [a]lphabetic, [n]umeric, [t]ime. A/N/T means reversed:"
what)
(setq sorting-type (read-char-exclusive)))
(let ((dcst (downcase sorting-type))
extractfun comparefun)
;; Define the appropriate functions
(cond
((= dcst ?n)
(setq extractfun 'string-to-number
comparefun (if (= dcst sorting-type) '< '>)))
((= dcst ?a)
(setq extractfun (if with-case (lambda(x) (org-sort-remove-invisible x))
(lambda(x) (downcase (org-sort-remove-invisible x))))
comparefun (if (= dcst sorting-type)
'string<
(lambda (a b) (and (not (string< a b))
(not (string= a b)))))))
((= dcst ?t)
(setq extractfun
(lambda (x)
(if (or (string-match org-ts-regexp x)
(string-match org-ts-regexp-both x))
(org-float-time
(org-time-string-to-time (match-string 0 x)))
0))
comparefun (if (= dcst sorting-type) '< '>)))
(t (error "Invalid sorting type `%c'" sorting-type)))
(sort (mapcar (lambda (x) (cons (funcall extractfun (car x)) (cdr x)))
table)
(lambda (a b) (funcall comparefun (car a) (car b))))))