Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
425 views
in Technique[技术] by (71.8m points)

emacs - Sort a mixed list of headings in org mode

I have a long list of headings in org-mode:

* Tasks [/]
** TODO Foo
** TODO Bar
** DONE World
** DONE Abba

that I want to sort as follows:

* Tasks [/]
** TODO Bar
** TODO Foo
** DONE Abba
** DONE World

With org-sort-entries I can either obtain

* Tasks [/]
** DONE Abba
** TODO Bar
** TODO Foo
** DONE World

(i.e. alphabetical order), or

* Tasks [/]
** TODO Foo
** TODO Bar
** DONE World
** DONE Abba

(i.e. grouping according to the status).

In other words, I want to sort the TODO and the DONE items alphabetically, but keep them in two blocks. How could I achieve it? I want to keep the whole set of headings in the same subtree!

Edit:

I didn't manage to utilize the suggestions below. So, I tried to use the tips provided below to come up with the solution I need. Here is the code I have:

(defun drorata-sort-TODO-DONE-headings ()
  (interactive)
  (save-excursion
    ;; Sort according to the TODO/DONE keywords
    (org-sort-entries t ?o)
    ;; Now there is a block of TODO's and a block of DONE's
    ;; Mark the TODO's
    (next-line)
    (beginning-of-line)
    (set-mark-command nil)
    (search-forward-regexp "[*].* DONE" nil t)
    (beginning-of-line)
    ;; Sort the marked region (of TODO's) alphabetically
    (org-sort-entries t ?a)
    ;; Now its time to sort the DONE's
    (search-forward "[*].* DONE" nil t)
    (beginning-of-line)
    (set-mark-command nil)
    ;; How can I find all headings labeled with DONE in the current level?
    )
)

My idea is to mark the headings that are labeled by TODO and sort them alphabetically and then do the same to the DONE headings. So far, I have it working only for the TODO's...

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

THE SHORT ANSWER

  • STEP # 1:? Place the cursor on the parent.

  • STEP # 2:? M-x org-sort-entries RET a RET

  • STEP # 3:??M-x org-sort-entries RET o RET

  • STEP # 4:? Crack open your favorite beverage and have a drink.


THE LONG-WINDED ANSWER

To expand upon the answer of @pmr (i.e., sorting a selected region), one may also wish to consider acting upon main headings to organize subheadings. For example, I like to perform a multi-sort -- first by alphabetic, second by todo-order, third by priority, and fourth by time. For subheadings that do not contain deadlines and contain only one type of todo status, it is sufficient for my needs to only sort by alphabetic. Set forth below is a sample function that I use to sort the entire buffer containing various main headings and subheadings. Here is the cheat-sheet from org-sort-entries:

Sort: [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.

NOTE: ?org-sort-entries uses the current-time when sorting entries based on time-stamp if the entry does not contain a time-stamp. The consequence of using the current-time is that undated entries will be sorted prior to future tasks containing time-stamps when sorting with the options t, c, s, or d. To weight the undated entries so that they are sorted after the dated entries, it is possible to use a later date than the current-time. The relevant let-bound variable is defined as (now (current-time)), and its usage within the function is written as (org-float-time now). This can be dealt with many different ways -- e.g., modifying the code containing (org-float-time now) with something containing an artificial date far off into the future -- e.g., (org-time-string-to-seconds "<2030-12-31 Tue>").

(defun lawlist-sort ()
  (when
      (save-excursion
        (goto-char (point-max))
        (re-search-backward "^\* CONTACTS" nil t)
        (re-search-forward  "^\*\* \(Planning\)" nil t))
    (goto-char (point-max))
    (re-search-backward "^\* CONTACTS" nil t)
    (org-sort-entries t ?a) )
  (when
      (save-excursion
        (goto-char (point-max))
        (re-search-backward "^\* DONE" nil t)
        (re-search-forward  "^\*\* \(None\)" nil t))
    (goto-char (point-max))
    (re-search-backward "^\* DONE" nil t)
    (org-sort-entries t ?a) )
  (when
      (save-excursion
        (goto-char (point-max))
        (re-search-backward "^\* UNDATED" nil t)
        (re-search-forward  "^\*\* \(Someday\)" nil t))
    (goto-char (point-max))
    (re-search-backward "^\* UNDATED" nil t)
    (org-sort-entries t ?a) )
  (when
      (save-excursion
        (goto-char (point-max))
        (re-search-backward "^\* EVENTS" nil t)
        (re-search-forward  "^\*\* \(Reference\|Delegated\|Postponed\|Waiting\)" nil t))
    (goto-char (point-max))
    (re-search-backward "^\* EVENTS" nil t)
    (org-sort-entries t ?a)
    (org-sort-entries t ?o)
    (org-sort-entries t ?p)
    (org-sort-entries t ?t) )
  (when
      (save-excursion
        (goto-char (point-max))
        (re-search-backward "^\* TASKS" nil t)
        (re-search-forward "^\*\* \(Active\|Next Action\|Hold\|Canceled\)" nil t))
    (goto-char (point-max))
    (re-search-backward "^\* TASKS" nil t)
    (org-sort-entries t ?a)
    (org-sort-entries t ?o)
    (org-sort-entries t ?p)
    (org-sort-entries t ?t) ) )

Here is an example of how to sort and reorganize an entire buffer containing main headings and subheadings. This is especially useful if the user desires synchronization with the Toodledo server using the org-toodledo library: https://github.com/christopherjwhite/org-toodledo ?To speed up the process when reorganizing a buffer containing hundreds of subheadings, the user may wish to consider suppressing the messages by modifying the functions responsible -- however, that is beyond the scope of this answer.

(setq org-todo-keywords '(
  (sequence
  "Active(a)"
  "Next Action(n)"
  "Canceled(c)"
  "Hold(h)"
  "Reference(r)"
  "Delegated(d)"
  "Waiting(w)"
  "Postponed(P)"
  "Someday(s)"
  "Planning(p)"
  "|"
  "None(N)") ))

(defun lawlist-reorganize ()
(interactive)
  (with-current-buffer (get-buffer "test.org")
    (setq buffer-read-only nil)
    (lawlist-refile-tasks)
    (lawlist-refile-events)
    (lawlist-refile-undated)
    (lawlist-refile-contacts)
    (lawlist-refile-done)
    (lawlist-sort)
    (goto-char (point-min))
    (save-buffer)
    (setq buffer-read-only t)))

(defun lawlist-refile-tasks ()
(interactive)
  (let* (
      (org-archive-location "/Users/HOME/Desktop/test.org::* TASKS")
      (org-archive-save-context-info nil))
    (goto-char (point-min))
    (unless (re-search-forward "^\* TASKS" nil t)
      (goto-char (point-max))
      (insert "* TASKS

"))
    (goto-char (point-max))
    (while
        (re-search-backward
          "^\*\* \(Active\|Next Action\|Hold\|Canceled\)" nil t)
      (org-archive-subtree))))

(defun lawlist-refile-events ()
  (let* (
      (org-archive-location "/Users/HOME/Desktop/test.org::* EVENTS")
      (org-archive-save-context-info nil))
    (goto-char (point-min))
    (unless (re-search-forward "^\* EVENTS" nil t)
      (goto-char (point-max))
      (insert "* EVENTS

"))
    (goto-char (point-max))
    (while
        (re-search-backward
          "^\*\* \(Reference\|Delegated\|Postponed\|Waiting\)" nil t)
      (org-archive-subtree))))

(defun lawlist-refile-undated ()
  (let* (
      (org-archive-location "/Users/HOME/Desktop/test.org::* UNDATED")
      (org-archive-save-context-info nil))
    (goto-char (point-min))
    (unless (re-search-forward "^\* UNDATED" nil t)
      (goto-char (point-max))
      (insert "* UNDATED

"))
    (goto-char (point-max))
    (while
        (re-search-backward
          "^\*\* \(Someday\)" nil t)
      (org-archive-subtree))))

(defun lawlist-refile-done ()
  (let* (
      (org-archive-location "/Users/HOME/Desktop/test.org::* DONE")
      (org-archive-save-context-info nil))
    (goto-char (point-min))
    (unless (re-search-forward "^\* DONE" nil t)
      (goto-char (point-max))
      (insert "* DONE

"))
    (goto-char (point-max))
    (while
        (re-search-backward
          "^\*\* \(None\)" nil t)
      (org-archive-subtree))))

(defun lawlist-refile-contacts ()
  (let* (
      (org-archive-location "/Users/HOME/Desktop/test.org::* CONTACTS")
      (org-archive-save-context-info nil))
    (goto-char (point-min))
    (unless (re-search-forward "^\* CONTACTS" nil t)
      (goto-char (point-max))
      (insert "* CONTACTS

"))
    (goto-char (point-max))
    (while
        (re-search-backward
          "^\*\* \(Planning\)" nil t)
      (org-archive-subtree))))

Here is a sample clean-up function to put proper spacing between entries and delete any empty lines at the end of the buffer -- the regexp assumes headings and subheadings are all flush-left:

(defun lawlist-cleanup ()
(interactive)
  (let ((query-replace-lazy-highlight nil))
    (replace-regexp "
+\*\* " "

** " nil (point-min) (point-max))
    (replace-regexp "
+\* " "


* " nil (point-min) (point-max))
    (goto-char (point-max))
    (delete-blank-lines)
    (let ((trailnewlines (abs (skip-chars-backward "
"))))
      (if (> trailnewlines 0)
        (delete-char trailnewlines))) ))

This solution does not rely upon selecting regions, but instead acts upon each main heading and organizes everything under that main heading -- i.e., all subheadings are organized. The code in this answer will regroup entries by refiling them underneath the correct main heading. For example -- if a task has been completed, the completed subheading will be ** None -- the code will look for all subheadings with ** None and move them to the main heading of * DONE, and then sort them alphabetically.

The main headings are: * TASKS; * EVENTS; * SOMEDAY; * CONTACTS; * DONE.

The following subheadings were chosen because they are a method of Getting Things Done, and the Toodledo server uses this same methodology: ?** Active; ** Next Action; ** Hold; ** Canceled; ** Reference; ** Delegated; ** Postponed; ** Waiting; ** Someday; ** Planning; ** None.

* TASKS

  ** Active

  ** Next Action

  ** Hold

  ** Canceled

* EVENTS

  ** Reference

  ** Delegated

  ** Postponed

  ** Waiting

* SOMEDAY

  ** Someday

* CONTACTS

  ** Planning

* DONE

  ** None

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...