Cycling focus to the next item with a given tag or itemPath

I want to cycle through items with a specific tag, and display each item focused in (without the searchbar being used).

Once the last item is reached, the cycle starts at the first item again.

Example:

  • Project 1:
    • Task A @next
  • Project 2:
    • Task B @next
  • Project 1:
    • Task C @next

Executing the script once would result in this being focused in:

  • Task A @next

Executing the script a second time, would result in this being focused in:

  • Task B @next

Executing the script again would result in this being focused in:

  • Task C @next

Executing the script a fourth time would result in this being focused in:

  • Task A @next

And so on.

Is the above plausible?

Does this overlap with what you need ?

Script: generic traffic-light cycling for tags and values - TaskPaper


Ah. Got it, it’s focus that you are cycling, within a set defined by a shared and unchanging tag.

I’ll take a look later today.

1 Like

Yep!

This is what I have to start with, which focuses the first item with a @today tag:

function TaskPaperContext(editor, options) {
    let outline = editor.outline
    let task = outline.evaluateItemPath('@today')[0]
    editor.focusedItem = task
}

Application('TaskPaper').documents[0].evaluate({
  script: TaskPaperContext.toString()
});

A possible draft:

Expand disclosure triangle to view JS source
(() => {
    "use strict";

    // TaskPaper 3 focus cycled to the next item
    // with a given tag.

    // Rob Trew @2022
    // Ver 0.05

    // --------------------- OPTIONS ---------------------

    const tagName = "today";


    // ---------- TASKPAPER EVALUATION CONTEXT -----------

    const tp3Context = (editor, options) => {
        const outline = editor.outline;

        // All tasks tagged with options.tagName,
        // and their ids.
        const
            taggedItems = outline.evaluateItemPath(
                `//@${options.tagName}`
            );

        // Any current focus item, and its position, 
        // if any, in the cycle.
        const
            maybeFocusedItem = editor.focusedItem,
            maybePosn = null !== maybeFocusedItem ? (
                taggedItems.findIndex(
                    x => maybeFocusedItem.id === x.id
                )
            ) : -1;


        // Zero-based index of next position in the cycle,
        // and the item at that index.
        const
            nextIndex = (1 + maybePosn) % taggedItems.length,
            nextFocusItem = taggedItems[nextIndex];

        return 0 < taggedItems.length ? (
            // Effect
            editor.focusedItem = nextFocusItem,
            // Value
            nextFocusItem.bodyContentString
        ) : `No items tagged as "@${options.tagName}".`;
    };

    // ------------- JXA EVALUATION CONTEXT --------------

    // main :: IO ()
    const main = () => {
        const
            doc = Application("TaskPaper")
            .documents.at(0);

        return doc.exists() ? (
            doc.evaluate({
                script: `${tp3Context}`,
                withOptions: {
                    tagName
                }
            })
        ) : "No document open in TaskPaper.";
    };

    // MAIN ---
    return main();
})();
1 Like

Your draft is working well—thank you @complexpoint !

1 Like

If it is not asking too much—I want to make a variation of the above that cycles through focused in projects (as opposed tagged tasks).

The common identifier for the projects is that they all begin with an em dash.

Example:
— Test Project:

I am starting with:

taggedItems = outline.evaluateItemPath(
                `/project \"—\"')`
            );

Am I on the right track?

I notice you are still using the name taggedItems in your snippet – are you hoping to cycle document focus through:

  • all projects in the document (regardless of tagging) ?
  • items that have a given tag, but only if their type is project ?
  • projects, but only if their name begins with an em-dash ?

Or perhaps something slightly different ?

In any case, generalizing to item paths (rather than tag names), and allowing for indented projects (as well as full-left projects) a first draft might look something like:

Expand disclosure triangle to view JS source
(() => {
    "use strict";

    // TaskPaper 3 focus cycled to the next item
    // matching a given item path.

    // Rob Trew @2022
    // Ver 0.01

    // --------------------- OPTIONS ---------------------

    // eslint-disable-next-line quotes
    const itemPath = '//project "—"';


    // ---------- TASKPAPER EVALUATION CONTEXT -----------

    const tp3Context = (editor, options) => {
        const outline = editor.outline;

        // All tasks on options.itemPath
        const
            cycleItems = outline.evaluateItemPath(
                `${options.itemPath}`
            );

        // Any current focus item, and its position,
        // if any, in the cycle.
        const
            maybeFocusedItem = editor.focusedItem,
            maybePosn = null !== maybeFocusedItem ? (
                cycleItems.findIndex(
                    x => maybeFocusedItem.id === x.id
                )
            ) : -1;


        // Zero-based index of next position in the cycle,
        // and the item at that index.
        const
            nextIndex = (1 + maybePosn) % cycleItems.length,
            nextFocusItem = cycleItems[nextIndex];

        return 0 < cycleItems.length ? (
            // Effect
            editor.focusedItem = nextFocusItem,
            // Value
            nextFocusItem.bodyContentString
        ) : `No items with with path ${options.itemPath}`;
    };

    // ------------- JXA EVALUATION CONTEXT --------------

    // main :: IO ()
    const main = () => {
        const
            doc = Application("TaskPaper")
            .documents.at(0);

        return doc.exists() ? (
            doc.evaluate({
                script: `${tp3Context}`,
                withOptions: {
                    itemPath
                }
            })
        ) : "No document open in TaskPaper.";
    };

    // MAIN ---
    return main();
})();

Newbie mistake on my part.

For this variation, I am looking for all projects in the document (regardless of tagging), but only if their line begins with an em dash.

Got it.

Does that second draft seem to work, in that context ?

It looks like you nailed it on the first draft!

Thank you so much @complexpoint !

1 Like

On my preference for a pedantic shift from taggedItems to cycledItems, this is food for thought on the legibility (weeks or months later, or by a subsequent editor) of names in code:

German Naming Convention

1 Like

For others that may be looking at the above, here is how I am using them.

Once a week, I do a weekly review of all of my tasks and projects.

During the review, I bo my best to identify the three projects that will provide the best benefits to my life, if I complete them in the forthcoming week. I mark these projects with an em dash, which makes them stand out

I do the same for three tasks, but I tag them with @today

As tasks and projects are completed, I do a mini-review and choose new ones.

During the week, I need to look at the highlighted tasks and projects, and use my intuition, or “gut instinct” to choose the one that I will focus on at any given moment.

I was looking at a list of all of them, but I found it difficult to focus and choose one.

But, I found that cycling through them one by one, allowed me to choose more intuitively. By seeing one after the other, in a cycle, my gut instinct kicks into gear. During the cycle, one always “stands out.”

The above scripts will now help me efficiently cycle through each at the touch of one macro key.

These are milestone improvements to my productivity system—I hope the above is of help to others!

Script added to wiki

3 Likes