TaskPaper 3: Child-aware Archiving

TaskPaper 3 is a critical part of my workflow.1 I use it to track what's happening now and what's up next, but also what I've done recently. Having a log of recent tasks is great during times of retrospection or when timeline questions come up.

In TaskPaper, completed tasks are tagged with @done. The built-in "Archive" action moves all done tasks to an "Archive" Project. Unfortunately, Archive will also disconnect child tasks from their parents:

Fotunately, TaskPaper is scriptable with JavaScript, and we can create a custom action with archiving more to my liking.

Goals

Ideally, we would not move child tasks to the Archive until their parent task is complete. The default behavior disconnects children from their parents, losing important context. Here's a document before archiving:

Inbox:
  - Build login page @due(2018-12-01)
    - Add auth0 library to composer.json @done(2018-08-17)
    - Write tests
    - Create tables
  - Book hotel for trip to sf @done(2018-06-10)
    - Get corporate card @done(2018-06-10)

And, after archiving:

Inbox:
  - Build login page @due(2018-12-01)
    - Write tests
    - Create tables
Archive:
  - Add auth0 library to composer.json @done(2018-08-17)
  - Book hotel for trip to sf @done(2018-06-10)
    - Get corporate card @done(2018-06-10)

Our history no longer tells us why the auth0 task existed. My preference is to keep the hierarchy, and only archive the "top-level" tasks that sit directly below a Project.

The "Smart Archive" extension

Here's an extension that accomplishes our goal.

  1. Ensure an "Archive" project exists
  2. Find all @done tasks that are immediate children of projects (excluding the Archive project)
  3. Move these items to the Archive, preserving their current order in the document
var TaskPaper = Application('TaskPaper');

function fn(editor, options) {
    var outline = editor.outline;
    var archive = outline.evaluateItemPath("//Archive:")[0];

    var insertBeforeElement = archive ? archive.firstChild : undefined;

    outline.groupUndoAndChanges(() => {
        if (typeof archive === "undefined") {
            archive = outline.createItem("Archive:");
            outline.root.appendChildren(archive);
        }

        var topLevelItems = outline.evaluateItemPath("project/@done except Archive//*")

        topLevelItems.map(item => {
            item.removeFromParent();
            if (insertBeforeElement) {
                archive.insertChildrenBefore(item, insertBeforeElement)
            } else {
                archive.appendChildren(item);
            }
        });
    });
}

TaskPaper.documents[0].evaluate({
    script: fn.toString()
})

If we install this script to the TaskPaper Scripts folder, we can quickly run it using cmd-shift-P and typing the script name. I find it's also helpful to rebind the default Archive shortcut in the Keyboard preference pane to combat muscle memory.

For more information about TaskPaper scripting, see the Scripting API, Extensions Wiki, and script installation guide.

  1. For more about why I like and how I use TaskPaper, see IFTTT and Taskpaper TODO Workflow.