Archive @done customizations?

Now that the archive @done script is part of the app, I’m having a hard time finding it to see what kinds of customizations might be done.

Basically, I’d like to bring the .todo header along when a completed task moves to the Archive. For example, if I mark this task done:

# Big Project.todo - task 1 @read

I’d like to see this in the Archive:

# Archive
## Big Project.todo
    - task 1 @read @done(2014-08-01)

Does that sound feasible? It’s probably outside of my trial and error script knowledge, but thought I’d see if others are interested in this kind of thing.

You can find the archive done command in the ft/modes/todo/todomode.js file. This is what the current implementation looks like:

Extensions.addCommand({
  name: 'archive @done',
  description: 'Move @done items to the archive section.',
  performCommand: function (editor, options) {
    var tree = editor.tree();

    tree.beginUpdates();
    tree.ensureClassified();

    var archiveNode = tree.evaluateNodePath("//@line:text = Archive")[0];
    if (!archiveNode) {
      archiveNode = createArchiveNode(tree);
      tree.appendNode(archiveNode);
    }

    Node.commonAncestorsForNodes(tree.evaluateNodePath("//@done except //@line:text = Archive//@done")).forEachNodeInSet(function(node) {
      archiveNode.insertChildBefore(node, archiveNode.firstChild);
    });

    tree.endUpdates();
  }
});

Does anyone have an easy way to archive an item with that item’s enclosing project name (if there is one)?

Thanks in advance for any responses…

I think this script will do what you want. I modified the above example to work as an AppleScript… and then changed the part where done nodes are moved to the archive, to also tag the done node with it’s previous heading if it exists.

tell application "FoldingText"
  tell front document
    evaluate script "function(editor, options) {
      var Node = require('ft/core/node').Node,
        tree = editor.tree();

      tree.beginUpdates();
      tree.ensureClassified();

      var archiveNode = tree.evaluateNodePath('//@line:text = Archive')[0];
      if (!archiveNode) {
        archiveNode = tree.createNode('# Archive');
        tree.appendNode(archiveNode);
      }

      Node.commonAncestorsForNodes(tree.evaluateNodePath('//@done except //@line:text = Archive//@done')).forEachNodeInSet(function(node) {
        var headingAncestor = node.evaluateNodePath('ancestor::@type heading')[0];
        archiveNode.insertChildBefore(node, archiveNode.firstChild);
        if (headingAncestor) {
          node.addTag('heading', headingAncestor.text());
        }
      });

      tree.endUpdates();
    }" with options {}
  end tell
end tell

Thanks, Jesse.

One thing: am I the only person whose archive is a “project”? As in “# Archive:”?

I tweaked things a little so I don’t get a new archive every time I run it, but aside from that, this works perfectly. Much appreciated.

Jesse, Is there a command to do a copy rather than a move of the done item (or whatever tag I want to use) , as that would be useful for repeating tasks so that I have a copy archived and can then toggle off the existing original item to be re used?

Thanks.

This is an interesting idea. Can probably be scripted— something that would copy the currently selected task, mark it as done (preferably including a done-date) and move it to the archive…

I’m still keeping an eye out for different ways of dealing with repeating tasks. Currently, I have a short list of tasks in a daily routine that I’ve captured as a TextExpander snippet and simply unpack each morning…

Trying to update this script to append “done” items to the end of the archive, rather than the beginning.

I’d hoped it would be as easy as changing this:

archiveNode.insertChildBefore(node, archiveNode.firstChild);

to this:

archiveNode.insertChildAfter(node, archiveNode.lastChild);

But that doesn’t seem to work. Can anyone point me in the right direction, please?

Thanks in advance!

@JSLR Instead try:

archiveNode.appendChild(node, archiveNode.lastChild);

@JSLR

Just realized I didn’t properly edit that. All you really need is:

archiveNode.appendChild(node);

Thanks Jesse. I appreciate the follow-up.

Hey Jesse— apologies for reviving the old thread. This script tags each archived item with the project it belonged to. Is there any way to add a similar tag if the task is in a sub-project (e.g. under a level 2 heading)?

Yes, I think so… but can you give me a concrete example. Ie sample file before and after the archive operation? I think the change will be somewhere around:

var headingAncestor = node.evaluateNodePath('ancestor::@type heading')[0];

Note that that is just grabbing the first [0] heading ancestor and them making a tag from it. To add more tags you’d need to process more of those heading ancestor results.

Jesse

Ah— got it. Thanks for pointing me in the right direction. Implemented a small tweak that does exactly what I need.

Hey Mark— Did you ever figure this out? I’ve been looking at the Archive/done script recently, and just started to give this “archive recurring item” thing some further thought, but I can’t figure out the logic/function for duplicating a node to be able to shift a copy to the archive…

This is as far as I’ve got with an archive @done customisation for catching persistent items or repeating tasks. It’s not scheduled recurrence; the script is supposed to duplicate an item tagged with ‘recur’ and dump a copy of the item in the archive, while keeping the original item in place. I say “supposed to” because I haven’t figured out an elegant way of doing this, and the script currently returns the copied recurring item to the end of its parent list.

(This also includes my tweak for catching tags for sub-projects).

tell application "FoldingText"
tell front document
	evaluate script "function(editor, options) {
  var Node = require('ft/core/node').Node,
    tree = editor.tree();

  tree.beginUpdates();
  tree.ensureClassified();

  var archiveNode = tree.evaluateNodePath('//@line:text = Archive:')[0];
  if (!archiveNode) {
    archiveNode = tree.createNode('# Archive:');
    tree.appendNode(archiveNode);
  }

  Node.commonAncestorsForNodes(tree.evaluateNodePath('//@done except //@line:text = Archive://@done')).forEachNodeInSet(function(node) {
    var headingAncestor = node.evaluateNodePath('ancestor::@type heading')[0];
    // JSLR: ADDED THE FOLLOWING VARIABLE TO CAPTURE TAGS FOR SUB-PROJECTS
   var headingAncestorSub = node.evaluateNodePath('ancestor::@type heading')[1];
	 if (node.hasTag('recur')) {
	 var pNode = node.parent;
	 var aRecur = tree.createNode(node.line());
	 aRecur.removeTag('done');
	pNode.appendChild(aRecur);
	 archiveNode.appendChild(node);
	 node.removeTag('recur');
	  }
    archiveNode.appendChild(node);
    if (headingAncestor) {
      node.addTag('project', headingAncestor.text());
    }
// JSLR: RENAME 'PROJECT' (e.g. AS subProject) IN THE CONDITIONAL STATEMENT BELOW IF YOU WANT TAGS FOR BOTH THE TOP LEVEL ANCESTOR AND THE SUB-PROJECT TO BE CAPTURED, OTHERWISE THIS REPLACES A LEVEL 1 HEADER WITH A LEVEL 2 HEADER, WHERE A LEVEL 2 HEADER IS PRESENT
if (headingAncestorSub) {
      node.addTag('project', headingAncestorSub.text());
    }
  });

  tree.endUpdates();
}" with options {}
end tell
end tell