0

Trying to create a single key that sorts a specific tree by date and then refreshes the agenda view.

(I have other dependent functions such that I want to sort the actual tree and not just the agenda view.)

Initial state

*Unrelated Tree
** TODO Not Sorted 1
  DEADLINE: <2021-09-17>      
** TODO Not Sorted 2
  DEADLINE: <2021-09-14>
* SortThis
** TODO Task A
  DEADLINE: <2021-09-16>
** TODO Task B
  DEADLINE: <2021-09-15>
=======================
Agenda 09-14
Task:    Deadline:    TODO Not Sorted 2
Task:    Deadline:    TODO Task B        => <2021-09-15>
Task:    In 2d:       TODO Task A
Task:    In 3d:       TODO Not Sorted 1

Desired State after key press

*Unrelated Tree
** TODO Not Sorted 1
  DEADLINE: <2021-09-17>
** TODO Not Sorted 2
  DEADLINE: <2021-09-14>
* SortThis
** TODO Task B
  DEADLINE: <2021-09-15>
** TODO Task A
  DEADLINE: <2021-09-16>
    
=======================
Agenda 09-14
Task:    Deadline:    TODO Not Sorted 2
Task:    In 1d:       TODO Task B        
Task:    In 2d:       TODO Task A
Task:    In 3d:       TODO Not Sorted 1

Starting Code based on Sort entire hirearchy in an Org mode buffer

(defun sort-all-org-entries-redo ()
  (interactive)
  (let ((fun #'(lambda nil
                 (condition-case nil
                     (org-sort-entries nil ?d)
                   (user-error t)))))
    (org-map-entries fun))
    (org-agenda-redo ()))
(add-hook 'org-agenda-mode-hook 
    (lambda()
        (local-set-key (kbd "g") 'sort-all-org-entries)))

Current issues

  1. Sort function works on entire buffer, not a specific tree. How can I make it apply to just a single tree?

  2. Function does not work between buffers. g in agenda buffer refreshes the agenda but does not sort the task buffer. M-x sort-all-org-entries performs the sort but does not refresh agenda. Is it even possible for a command to span separate buffers?

1
  • What's the question?
    – Drew
    Sep 17 at 18:14
0

Q1: If you look at the description of org-map-entries (with C-h f org-map-entries), you will see that it takes optional arguments, one of which is scope, which can take a bunch of different values, one of which is tree. The description says this:

   SCOPE determines the scope of this command.  It can be any of:

     ‘nil’
          The current buffer, respecting the restriction, if any.

     ‘tree’
          The subtree started with the entry at point.
      ...

Forgetting the agenda stuff, try changing the function to this:

(defun my/sort-subtree-org-entries ()
  (interactive)
  (let ((fun (lambda ()
                 (condition-case nil
                     (org-sort-entries nil ?d)
                   (user-error t)))))
    (org-map-entries fun nil 'tree)))

Then position the cursor at the start of the subtree you want to sort and say M-x my/sort-subtree-org-entries.

Note that it is important to be at the right place when you invoke the function. If you place the cursor at the beginning of the "Unrelated tree" and invoke the function, the "Unrelated tree" will be sorted. It is up to you to make sure that you are at the right position in the buffer when you invoke the function.

Q2: Of course it is possible for a command to "span separate buffers" as you put it, but it does not happen magically: you (i.e. your function) have to make sure that the context is correct for each operation. You do that by selecting the correct buffer and moving point to the right place in the buffer (if necessary).

In particular, if you look at the doc string of org-agenda-redo with C-h f org-agenda-redo, you will see that it says Rebuild possibly ALL agenda view(s) **in the current buffer**. IOW, you have to make sure that you are in the agenda buffer when you evaluate the call to org-agenda-redo. Similarly, you have to be in the file buffer where the entries to be sorted are, before you invoke my/sort-subtree-org-entries. Moreover, you have to be in the right place in the file buffer (at the beginning of the subtree to be sorted) before you invoke it.

I'll do the easy part: you are in the file buffer and you want to sort a subtree and regenerate the agenda. I don't want to modify the sort function above to do two things, because the one thing that it does is useful and I might want to do that alone, not in combination with the agenda. Instead, I want to write another function that selects the agenda buffer and regenerates the agenda, and a third function that composes these two: it sorts the subtree by calling the first function and then regenerates the agenda by calling the second function. Here's the code:

(defun my/switch-to-agenda-and-redo ()
   (with-current-buffer org-agenda-buffer
       (org-agenda-redo)))

(defun my/sort-subtree-regenerate-agenda ()
   (interactive)
   (my/sort-subtree-org-entries)
   (my/switch-to-agenda-and-redo))

Now you can put your cursor in the right place at the beginning of the subtree to be sorted and say M-x my/sort-subtree-regenerate-agenda.

I should point out that if you don't have an agenda buffer open, you are going to get an error: the my/switch-to-agend-and-redo function badly needs error handling, but the basic idea is there.

Going in the other direction (from the agenda, switch to the file buffer and move to the right point, sort and then regenerate the agenda) is harder, because you have to figure out what file buffer the entry came from, then figure out how far up the tree to go to get to the right place for sorting. I think there is enough information in the agenda to determine the file buffer, but I don't think there is enough information to figure out the subtree (let's say you want to sort the second level subtree, but the entry in the agenda is a fourth level entry: how will the function know to go up to the second level and not stop at the third?) So I think your specification of the problem is incomplete: you will somehow have to provide that information to the function.

BTW, the code is completely untested.

3
  • Verified answer 1 works as intended, and answers are appropriate for actual questions asked. Alternate direction for answer 2 resubmitted as new question
    – XLIII
    Sep 17 at 23:35
  • I believe with-buffer should be with-current-buffer
    – XLIII
    Sep 24 at 19:35
  • That's correct - fixed. Thank you!
    – NickD
    Sep 24 at 19:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.