Skip to content

Task Sorting: allow to run from templates with parameters (without asking user) #618

Open

Description

@jgclark

The Todo List for the plugin says "Sort tasks via template call". I'm asking for some of that to be included. In particular for just the "Sort tasks under heading (choose)" command.

At the moment running this template tag
<% await DataStore.invokePluginCommandByName("sortTasksUnderHeading","dwertheimer.TaskSorting",["Home"]) -%>
requires me to select Heading and then sort order.
I want to be able to pass these in as parameters to avoid these questions popping up when running in template.

Currently running v1.1.0.

Related bugs

  1. The documentation doesn't mention the available command "Sort tasks on the page "
  2. Note that in the plugin.json the command name includes a space at the end. This will make it difficult to use it correctly from template tags.

Activity

  1. added
    bugSomething isn't working
    documentationImprovements or additions to documentation
    enhancementNew feature or request
    on Jan 19, 2025
  2. dwertheimer commented on Jan 20, 2025

    @dwertheimer
    Collaborator

    @jgclark I made a tweak that will allow it to work as an xcallback. See: here

    That said, there may challenges with running commands like this in a Template render, because this command acts on Editor but Editor is in flux during a template rendering. The note content is effectively read and re-rendered and then replaced. So I'm not sure that you can reliably run a template command that rewrites the Editor contents (like this one does) and get consistent results.

    Please build dwertheimer.TaskSorting and try it out. Happy to discuss further based on what you find.

  3. jgclark commented on Jan 24, 2025

    @jgclark
    CollaboratorAuthor

    Thanks. As it happens my first use is from another plugin command, not actually a template tag.
    I want /generate repeat to call /tsd after it has made changes. So I have set that up, and rebuilt TS&S to 1.2.0.

    It's logging a lot (most of which I don't understand), but it's not appearing to have any effect.
    I've now compared the output from the Repeats run to when I invoke it manually on the same short ### Main section with 7 tasks -- attached. They're almost identical, with the most differences being single digits on how many lines etc., which I think are a factor of my testing. They both start the same, and have correctly picked up my settings:
    Starting sortTasks(withUserInput:false, default sortFields:["due","-priority","content"], withHeadings:false, byHeading:true):

    pluginCommandCall.log
    UserCall.log

    FYI there are things that don't make a lot of sense to me in the logs are:

    • why when I have byHeading set, does it seem to do the same level of processing on the sections I'm not interested in?
    • what the Queuing 21 open tasks for deletion from note means, given I'm not asking for anything to be deleted.
      But they're probably incidental to the main point.
  4. dwertheimer commented on Jan 25, 2025

    @dwertheimer
    Collaborator

    @jgclark In the OP, you said you wanted to use "sortTasksUnderHeading" (/sth) and you gave an example of passing a heading, so that's the command that I refactored and wanted you to test. But in your most recent post, you say you wanted to use /tsd (Task Sort by Defaults), which is a different command and works on a whole note, and which I have not updated. It would make sense to me that /tsd would list all the tasks in the note, because it is sorting each heading's tasks separately under it's heading.

    Image

    So before I dig into try to figure out what is and isn't working, can you please restate the use case that you are trying to solve for?

    BTW, yes the "open tasks for deletion" language is a little unsettling, but it's technically correct, because all the tasks are removed from the note so that they can be rewritten in a different order.

  5. dwertheimer commented on Jan 25, 2025

    @dwertheimer
    Collaborator

    Also, just noticing that you wrote:

    Note that in the plugin.json the command name includes a space at the end. This will make it difficult to use it correctly from template tags.

    Can u pls send me a screenshot? I am not seeing this in plugin.json

  6. jgclark commented on Jan 25, 2025

    @jgclark
    CollaboratorAuthor

    Sorry, my bad. I had both of these ideas in my head originally, and I thought I'd asked about both of them.
    I'm giving this another go at my end.

  7. jgclark commented on Jan 27, 2025

    @jgclark
    CollaboratorAuthor

    I've done more on this, and pushed commit of WIP with RepeatExtensions 0.9, including a few minor tweaks to TaskSorting 1.2.0.
    I've now tried using both 'sortTasks' and 'sortTasksUnderHeading' functions via await DataStore.invokePluginCommandByName (using their command names) and both appear not to make changes in the Editor, despite lots of sensible-looking logging. (I emphasise that this is not from a Template, so we shouldn't be having race conditions from that.)
    I've tried putting an updateCache(Editor.note) before the sort calls, but that's not making a difference.
    I'm sure it's something very simple now.

  8. jgclark commented on Jan 27, 2025

    @jgclark
    CollaboratorAuthor

    For the following test section, which doesn't change after the sortTasks call (though the screen flickers, showing it is updated):

    ### Tasks
    * Task-Test-2 @repeat(2m) >2023-07
    * [x] Task-Test-2 @repeat(2m) >2023-05 @done(2025-01-27)
    * >2023-09-01 @repeat(1m) Task-test-5 text afterwards >2023-10-01
    * [x] >2023-09-01 @repeat(1m) Task-test-5 text afterwards >2023-12-13 @done(2025-01-27)
    * Task-Test-1 @repeat(2m) >2023-W47
    * [x] Task-Test-1 @repeat(2m) >2023-W39 @done(2025-01-27)
    

    The logged output is

    Starting sortTasks(withUserInput:false, default sortFields:["due","-priority","content"], withHeadings:false, byHeading:true):
    2025-01-27 09:11:41 | DEBUG | 	sortTasks: User chose sort=["due","-priority","content"]
    2025-01-27 09:11:41 | DEBUG | 	subHeadingCategory=false printSubHeadings=false  cat=none
    2025-01-27 09:11:41 | DEBUG | dwertheimer.TaskSorting v1.2.0 :: sortTasks about to get sortGroups object
    2025-01-27 09:11:41 | DEBUG | dwertheimer.TaskSorting v1.2.0 :: sortTasks have sortGroups object. key count=3. About to start the display loop
    2025-01-27 09:11:41 | DEBUG | sortTasks: heading Group title="__" (0 paragraphs)
    2025-01-27 09:11:41 | DEBUG | sortTasks: heading Group title="TEST Repeats" (1 paragraphs)
    2025-01-27 09:11:41 | DEBUG | 	1 total lines in section/note
    2025-01-27 09:11:41 | DEBUG | 	Open Tasks:0
    2025-01-27 09:11:41 | DEBUG | 	After Sort - Open Tasks:0
    2025-01-27 09:11:41 | DEBUG | dwertheimer.TaskSorting v1.2.0 :: writeOutTasks taskTypes: scheduled,checklistScheduled,open,checklist,done,checklistDone,cancelled,checklistCancelled
    2025-01-27 09:11:41 | DEBUG | writeOutTasks: writing task types in order : scheduled,checklistScheduled,open,checklist,done,checklistDone,cancelled,checklistCancelled
    2025-01-27 09:11:41 | DEBUG | sortTasks: heading Group title="Tasks" (4 paragraphs)
    2025-01-27 09:11:41 | DEBUG | 	4 total lines in section/note
    2025-01-27 09:11:41 | DEBUG | 	Open Tasks:0
    2025-01-27 09:11:41 | DEBUG | 	After Sort - Open Tasks:0
    2025-01-27 09:11:41 | DEBUG | 	Queuing 3 done tasks for deletion from note
    2025-01-27 09:11:41 | DEBUG | 	sortTasks/deleteExistingTasks :: Before Sort Lines in Note:23 | Lines to delete:3
    2025-01-27 09:11:41 | DEBUG | 	sortTasks/deleteExistingTasks :: After Sort Lines in Note:23 | Lines to delete:3
    2025-01-27 09:11:41 | DEBUG | 	sortTasks/deleteExistingTasks :: After Remove Paragraphs, Lines in note:20 -- OK (math lines up)
    2025-01-27 09:11:41 | DEBUG | dwertheimer.TaskSorting v1.2.0 :: writeOutTasks taskTypes: scheduled,checklistScheduled,open,checklist,done,checklistDone,cancelled,checklistCancelled
    2025-01-27 09:11:41 | DEBUG | writeOutTasks: writing task types in order : scheduled,checklistScheduled,open,checklist,done,checklistDone,cancelled,checklistCancelled
    2025-01-27 09:11:41 | DEBUG | 	writeOutTasks TASK_TYPE=done -- 3 tasks -- withHeadings=false
    2025-01-27 09:11:41 | DEBUG | 	InsertTodos: subHeadingCategory= typeof=string 3 todos
    2025-01-27 09:11:41 | DEBUG | 	insertTodos :: tasksToTop=false title="Tasks"
    2025-01-27 09:11:41 | DEBUG | paragraph/endOfFrontmatterLineIndex :: total paragraphs in note (lineCount) = 20
    2025-01-27 09:11:41 | DEBUG | NPParagraph / getParagraphBlock :: Starting at lineIndex 2 with start active/last line = 1/19 with true/true: 'Tasks'
    2025-01-27 09:11:41 | DEBUG | NPParagraph / getParagraphBlock ::   - Found 1 paras in block starting with: "Tasks"
    2025-01-27 09:11:41 | DEBUG | 	insertTodos note.insertParagraphAfterParagraph "Tasks"
    2025-01-27 09:11:41 | DEBUG | 	Finished writeOutTasks, now finished
    2025-01-27 09:11:41 | DEBUG | Finished sortTasks()!
    
  9. jgclark commented on Jan 27, 2025

    @jgclark
    CollaboratorAuthor

    In case it's relevant I've also now tried calling directly to sortTasks() rather than via DataStore.invokePluginCommandByName('Sort tasks on the page', ...). Same outcomes.

  10. jgclark commented on Aug 29, 2025

    @jgclark
    CollaboratorAuthor

    Can I ping you about this, please @dwertheimer ? I'm still keen to use /sth programmatically.

  11. dwertheimer commented on Aug 30, 2025

    @dwertheimer
    Collaborator

    @jgclark I have looked into this. It is definitely a classic Editor vs Editor.note race condition thing. Without changing any of the initial code...

    I created this test template, which functions like any plugin code sequence:

    <%- Editor.prependParagraph(`1. this content was prepended to Editor`,`text`) -%>
    <%- Editor.appendParagraph(`2. this content was appended to Editor`,`text`) -%>
    <% await DataStore.invokePluginCommandByName("Sort tasks under heading (choose)","dwertheimer.TaskSorting",["Tasks",`["due","-priority","content"]`])  %>
    

    When I invoke this code, it appears that the document has not been sorted. However, I can look at the revisions and see what happened.

    1. New content was added to Editor by the first two lines of code (this would be equivalent to your repeats plugin making its changes to Editor)

    2. Then, Task Sorter runs. tasks were deleted/added moved around by the TS plugin (which operates on Editor.note), and were sorted per the invoke/callback settings. You can actually see them sorted correctly.

    3. Then Editor saves and overwrites the changes with the original content (e.g. that your plugin wrote to the Editor), wiping out the sorted version. It happens so fast, you never see the sorted ones, because you are left with the original Editor save.

    Image

    If I change the commands that write to the note to Editor.note, then everything works exactly as you want. This is annoying and causes us to burn a lot of time because we never know what a plugin is using under the hood (Editor or Editor.note or just a DataStore.note). I hope you will add some comments to the request I made to @EduardMe to have an waitForCommit function.

    For this particular use-case I have added a work-around.

  12. dwertheimer commented on Aug 30, 2025

    @dwertheimer
    Collaborator

    @jgclark I am adding a 3rd parameter to this command (and will look at adding it to the others). This is the note in the README:

    NOTE: If you are calling this command from a plugin or a template and want to sort tasks under a heading in a specific note (or the Editor) you have been working on, you should pass the note as a third parameter to ensure that taskSorting is working on the same Object you have been working on, e.g. ("myHeading",["-priority","content"], Editor). For clarity, Task Sorting plugin works on Editor.note, but if you are using Editor (and not Note) you should pass Editor in as the noteOverride parameter, else the delayed write from Editor will overwrite any task sorting you do.

  13. dwertheimer commented on Aug 30, 2025

    @dwertheimer
    Collaborator

    @jgclark I have tested and this all works. However, the problem is that then, when you are running in the Editor (not on a note), then running on your example with @repeats doesn't work quite right because NP insists on popping up the "Are you sure you want to delete the repeating XXX" message for each of them, because in order to move them around, I delete them and re-insert them in the proper place. But the delays with the popup and waiting for an answer somehow result in odd duplications of content. It works perfectly if you have no @repeats.

    I was able to use the work-around that Eduard gave us Editor.skipNextRepeatDeletionCheck = true and I think that is properly suppressing the nag popups. So I will re-release and have you test it.

    Please let me know your thoughts. I will look at the rest of the functions on the plane this weekend.

  14. dwertheimer commented on Aug 30, 2025

    @dwertheimer
    Collaborator

    Here's the command I am running:

    await DataStore.invokePluginCommandByName("Sort tasks under heading (choose)","dwertheimer.TaskSorting",["Tasks",`["due","-priority","content"]`,Editor]) 
  15. EduardMe commented on Aug 31, 2025

    @EduardMe
    Contributor
  16. EduardMe commented on Aug 31, 2025

    @EduardMe
    Contributor
  17. jgclark commented on Sep 1, 2025

    @jgclark
    CollaboratorAuthor

    Thanks for working on this. I've now been able to bring it into my template. An observation and a question on this:

    Suggestion: Could you make a template-only entry point for this so we don't have to encode Sort tasks under heading (choose). I first tried sortTasksViaTemplate as that's what was mentioned in the CHANGELOG. This is what I do now to provide a specific easier way in for automation.

    Question: I ran it as
    <% await DataStore.invokePluginCommandByName("Sort tasks under heading (choose)","dwertheimer.TaskSorting", ["Home", ["-priority", "content"]]) -%>
    This works, but lands up sorting all checklists under all tasks. This isn't what I was expecting, which was higher priority tasks then checklists, then lower priority tasks then checklists.
    How can I do this?

  18. dwertheimer commented on Sep 18, 2025

    @dwertheimer
    Collaborator

    @jgclark I will have a look at this. To make sure I deliver on your expectations, can you please show me an example of what you would hope for instead? For instance if this is the source note:

    ## my heading
    + [x] a completed checklist
    * [x] a completed task
    + 01 a checklist
    * 02 a task
      some indented text under 02 a task
      + 03 !! a high priority checklist under 02 a task
        * 04 a task under 03 checklist
    * !! a high priority task
    * [x] !!! a super high priority completed task
    

    What would you want the output to be?
    Please feel free to add any other permutations that you think would be good to point out.

  19. jgclark commented on Sep 18, 2025

    @jgclark
    CollaboratorAuthor

    Thanks.

    In your cases I'd hope for:

    ## my heading
    * !! a high priority task
    * 02 a task
      some indented text under 02 a task
      + 03 !! a high priority checklist under 02 a task
        * 04 a task under 03 checklist
    + 01 a checklist
    * [x] !!! a super high priority completed task
    + [x] a completed checklist
    * [x] a completed task

    I.e. priority > open things > completed things > cancelled things. At the moment the function seems to always sort all sorts of checklists lower than any sort of task. But I'm wanting all open tasks or checklists to sort ahead of completed ahead of cancelled tasks/checklists.

  20. dwertheimer commented on Sep 19, 2025

    @dwertheimer
    Collaborator

    @jgclark I am updating /tsh to allow for arguments for each param.
    sample x-callback:

    noteplan://x-callback-url/runPlugin?pluginID=dwertheimer.TaskSorting&command=Sort%20tasks%20on%20the%20page&arg0=false&arg1=priority%2Ccontent&arg2=false&arg3=false
    

    I think maybe my test case was not what we needed to properly solve for your needs, because when I run /tsh on the test case I wrote above, I get exactly what you said you were hoping for.

    Image

    But I think I understand now what you were looking for, but I'm not 100% sure how to get it done.

    In this example:

    Image

    I notice that it doesn't seem that Eduard (or maybe just the theme) considers an indented checklist with !!! to actually be a priority.

    What would you expect to happen in this sort?
    I think you would expect the checklist with three !!! to sort to the very top
    but what should happen to the checklist that has three !!! but is indented under another item?

    It's always been the edges of sorting that have caused me challenges...
    Please let me know. Thanks!

  21. dwertheimer commented on Sep 19, 2025

    @dwertheimer
    Collaborator

    @jgclark The fact that tasks are sorted first by type and then by the given sort order is rather fundamental to how task sorter works. It may take some major surgery to interleave them. I will give this some thought. But the more info you can give me on the above the better.

  22. dwertheimer commented on Sep 19, 2025

    @dwertheimer
    Collaborator

    @jgclark I made some changes. sending to you to test.

  23. dwertheimer commented on Sep 19, 2025

    @dwertheimer
    Collaborator

    @jgclark Update: I made some major changes; have not had a change to fully test all the functions. Would appreciate it if you could use it for a bit before we unleash it.

    New Feature: Task Type Interleaving (Default Behavior)

    • By default, tasks are interleaved: compatible task types are combined and sorted together by priority
    • Within each priority level, open tasks appear before checklists
    • This allows tasks to be sorted by priority first, then by type (open before checklist)
    • To use traditional grouping (all open tasks together, then all checklists together), set interleaveTaskTypes=false
    Image

    I made "your way" of interleaving open+checklists the default, so you should start to see it working without changing anything in your template/callback calls.

    New version:

    [1.2.5] - 2025-09-13 (@dwertheimer)

    • Add logging for jgclark to sortTasksUnderHeading
    • Add ability to pass all params to /ts and /tsh commands
    • remove sortTasksViaTemplate code which was never a published command
    • change default behavior of /ts* commands to interleave task types (open/checklist together)

    dwertheimer.TaskSorting-v1.2.5.zip

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdocumentationImprovements or additions to documentationenhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions