Add @project tag to selected item(s)

The script below appends a tag in the form @project(project / sub-project / sub-sub-project) to each selected item, or does nothing if the item isn’t under any project.

The script is an extract of the JavaScript for TP3’s native Add @project tag when archiving @done tasks behaviour, which Jesse kindly posted here. I don’t know any JS and as Jesse notes, the code was translated automatically from CoffeeScript, so the syntax might be a bit inelegant.

Intended use case:

Current workflow: Each morning, triage tasks by assigning @today (or @today(am/pm)) to those I want to do that day.

Problem: The @today view presents a hierarchical outline of the day’s tasks, with project headings. This is great for maintaining context, but the list is difficult to skim, and re-ordering tasks moves them out of their projects. I think I’d prefer a flat list of the day’s tasks which I can re-order freely, but would like to retain their project metadata for archiving.

Proposed workflow: Triage tasks by moving them to a project (such as 2021-01-26: @today perhaps) rather than tagging, but use a macro to execute the script below first.

// From TaskPaper's native "Add @project tag when archiving @done tasks" behaviour:
// https://support.hogbaysoftware.com/t/how-to-build-the-add-project-tag-from-preferences/3225/3

function TaskPaperContextScript(editor, options) {
	let outline = editor.outline
	let selection = editor.selection
	
	outline.groupUndoAndChanges(() => {
		selection.selectedItems.forEach((item) => {
			if (projects = ((function() {
				var i, l, ancestors, result;
				ancestors = outline.evaluateItemPath('ancestor::@type=project', item);
				result = [];
				for (i = 0, l = ancestors.length; i < l; i++) {
					eachProject = ancestors[i];
					result.push(eachProject.bodyContentString);
				}
				return result;
			})()).join(' / '))

			item.setAttribute('data-project', projects);

		});
	})
	
	editor.moveSelectionToItems(selection)
}

Application("TaskPaper").documents[0].evaluate({
	script: TaskPaperContextScript.toString()
});
1 Like

Added to the wiki

1 Like

It looks fine to me :slight_smile:

FWIW, as lazy person, prone to careless mistakes with fiddly details, I find it easier to let .map deal with all the mechanics of working through a list, so I might just tend to switch to something like this:

// From TaskPaper's native "Add @project tag when archiving @done tasks" behaviour:
// https://support.hogbaysoftware.com/t/how-to-build-the-add-project-tag-from-preferences/3225/3

function TaskPaperContextScript(editor, options) {
    let outline = editor.outline
    let selection = editor.selection

    outline.groupUndoAndChanges(() => {
        selection.selectedItems.forEach(item => {
            // if (projects = ((function() {
            // var i, l, ancestors, result;
            // ancestors = outline.evaluateItemPath('ancestor::@type=project', item);
            // result = [];
            // for (i = 0, l = ancestors.length; i < l; i++) {
            //     eachProject = ancestors[i];
            //     result.push(eachProject.bodyContentString);
            // }
            const
                projects = outline.evaluateItemPath(
                    'ancestor::@type=project', item
                )
                .map(eachProject => eachProject.bodyContentString)
        
            if (0 < projects.length) {
                item.setAttribute(
                    'data-project', projects.join(' / ')
                )
            };
        });
    })

    editor.moveSelectionToItems(selection)
}

Application("TaskPaper").documents[0].evaluate({
    script: TaskPaperContextScript.toString()
});
2 Likes