I use a @now
tag as a bookmark to the task I am working on.
(TextBar displays the tagged task (with some parental/ancestral context) on the OS X menubar – see Script: displaying the active task in the OS X menu bar)
Here is a script to remove the friction of manually typing and deleting a @now
or @next
tag of this kind. It can be attached to a keystroke with something like Keyboard Maestro or FastScripts.
The script:
- Places a
@now
tag at the top of the outline if there is no such tag in the file - If there is already a
@now
tag somewhere, it just moves it on to the next task that is not @done, unfolding part of the outline to make the newly tagged line visible, if necessary.
That’s it, but also:
If you edit the 1 (or true) argument in the last line of the script to 0 (or false), you can save it as a second script which moves the tag upwards rather than downwards in the outline:
- to a preceding sibling if there is one,
- or to the immediate parent.
and
If you are using TextBar to display the line marked with @now
, you can also trying editing the 3rd argument to the main function (right at the end of the script) to 1 rather than 0.
This will force TextBar to update its display of your current task each time this script moves your @now
or @next
tag.
(It’s using a bit of an unsophisticated approach – if you are using TextBar, do write a quick note to its author to support the request for an Applescript .refresh()
method which can trigger an immediate refresh a little more rapidly and elegantly : - )
SCRIPT:
// Move @now or @next tag to next available item in file
// Ver 0.9
// Requires TP3 Preview (build 166+)
// Skips @done items
// If necessary, unfolds to make newly tagged item visible
// If blnMarkDone=true:
// 1. Marks the current item as @done(yyyy-mm-dd HH:mm)
// before moving the @now/@next tag
// 2. If the parent project is now completed, marks that as @done too
(function (strNowTag, blnDown, blnTextBar, blnMarkDone) {
// Adjust value for blnDown at foot of script:
// 0/false: Move UP (skipping @done items)
// 1/true : Move DOWN (skipping @done items)
// blnTextBar: true:force refresh of TextBar,
// false:not using Textbar, or don't force refresh
'use strict';
// Edit here to move a differently named bookmarker tag,
// like 'next' (--> @next)
var strTag = strNowTag || 'now';
var ds = Application("TaskPaper").documents,
blnMoved = ds.length ? ds[0].evaluate({
script: (
// Evaluated in TP3 ********************
function (editor, options) {
// projects ancestral to this item
// (plural because projects can nest)
// tpItem -> [tpProject]
function itemProjects(oItem) {
return oItem.evaluateItemPath(
'ancestor-or-self::@type=project'
);
}
// descendant tasks not @done ?
// tpItem -> [tpItem]
function tasksRemaining(oItem) {
return oItem.evaluateItemPath(
'descendant::(@type=task and not @done)'
);
}
// monadic bind/chain for lists
// [a] -> (a -> [b]) -> [b]
function chain(xs, f) {
return [].concat.apply([], xs.map(f));
}
var strTag = options.tag,
strAttrib = 'data-' + strTag,
oOutline = editor.itemBuffer.outline,
lstNow = oOutline.evaluateItemPath('//@' + strTag + '[0]');
if (lstNow.length) {
var oNow = lstNow[0],
blnFound = false,
blnDown = options.downward,
lstNext = chain(
blnDown ? [
'following', 'ancestor'
] : [
'preceding', 'ancestor/descendant'
],
function (x) {
if (blnFound) return [];
else {
var lst = oNow.evaluateItemPath(
x + '::(@type=task and not @done)[' +
(blnDown ? '0' : '-1') + ']'
),
lng = lst.length,
oNext = lng ? [lst[0]] : [];
blnFound = blnFound || lng;
return oNext;
}
}
),
oNext = lstNext.length ? lstNext[0] : null;
if (options.markDone) {
if (oNow.getAttribute('data-type') === 'task') {
var strTime = moment().format('YYYY-MM-DD HH:mm');
oNow.setAttribute('data-done', strTime);
// also mark any projects finished by this as done
itemProjects(oNow).filter(function (p) {
return tasksRemaining(p).length === 0;
}).forEach(function (x) {
x.setAttribute('data-done', strTime);
});
}
}
// clear any existing @now tags
lstNow.forEach(function (x) {
return x.removeAttribute(strAttrib);
});
}
var oTaggable = (oNext && !oNext.isRoot) ? oNext : (
function () {
var lstDoable = oOutline.evaluateItemPath(
'//(not @done)[' + (blnDown ? '0' : '-1') + ']'
);
return lstDoable.length ? lstDoable[0] : null;
}()
);
return oTaggable ? (
oTaggable.setAttribute(strAttrib, ''),
editor.isVisible(
oTaggable
) || editor.makeVisible(oTaggable),
true
) : false;
}
//**************************************
).toString(),
withOptions: {
downward: blnDown,
tag: strTag,
markDone: blnMarkDone
}
}) : false;
// If you are using [TextBar](http://www.richsomerfield.com/apps/)
// to display the active TaskPaper task on the OS X menu bar, as described in:
// http://support.hogbaysoftware.com/t/script-displaying-the-active-task-in-the-os-x-menu-bar/1290
// then uncommenting the following 4 lines
// will force an immediate update of TextBar
//*****************************************
// COMMENT OUT 12 LINES (TO NEXT LINE OF ASTERISKS) IF NOT USING TEXTBAR
if (blnTextBar && blnMoved) {
try {
var a = Application('TextBar');
['quit', 'activate'].forEach(
function (m) {
a[m]();
}
)
} catch (_) {
return 'Textbar not running';
}
}
//******************************************
if (!blnMoved) {
var a = Application.currentApplication();
(a.includeStandardAdditions = true, a).displayNotification(
'All done in active TP3 file !', {
withTitle: "TaskPaper 3 active tasks",
soundName: "Glass"
}
);
};
return blnMoved;
// EDIT VALUE FOR blnDown (see top of script)
// 0: Move tag up (skipping @done items)
// 1: Move tag down (skipping @done items)
// strTagName, blnDown, blnTextBar, blnMarkDone (1: true, 0:false)
})('now', true, true, false);