Looking for a script that deletes the first instance of a specific note and then saves the TaskPaper document

Hello all!

I’m looking for a script that deletes the first instance of a specific note and then saves the TaskPaper document.

Scope:

  • The document is: Focus.txt
  • The specific note is: :arrow_backward:︎ Walk → @now :arrow_forward:
  • The note could be anywhere in the document, including the root
  • note here means any row without leading bullet or trailing : ?
  • The blue-framed triangles are part of the row ? (or a parenthesising device for this post ?)
  • root in the TaskPaper scripting model is, I think, a virtual (textless) node – the parent of all (visible) top-level outline items – here you are thinking of a document which has only one top-level row ?
  • if a row is matched for deletion, and has further rows indented under it, are they deleted too ?
  • Yes, without the leading bullet or the trailing colon.
  • I am still playing with the format. The triangles are just ascii symbols. I am not sure why this forum made them blue.
  • In this case, I might be placing the note at the root of the document. See the screenshot below.
  • Good question. In my case, these will never have indented rows under them.

For the screenshot, I removed the symbols:

Screenshot 2024-11-16 at 11.42.33 PM

If deleting by the specific note is a challenge, an alternative would be to delete the first item that is tagged with @now.

Second alternative: deleting the first item in the document (as I am ok with placing the note at the top/root of the document).

If no one else gets there first, I’ll take a look this evening, but generally, I think:

  • You should be able to parameterise some arbitrary matching predicate – perhaps expressed in terms of an item path – to experiment with defining the first matching row in different ways.
  • I personally happen to be a bit superstitious and avoidant about sharing scripts which delete, so there again, it may make sense to parameterise the effect applied to the first matching row (if any), and offer you a choice of different effects (including deletion) to uncomment and experiment with :slight_smile:
  • Saving will need to be triggered outside the TaskPaper JSContext, and is well within reach of JXA, but probably worth suspending until you are absolutely sure that the deletions aren’t going to overshoot or otherwise surprise …

A rough draft of some elements here.

You would need to fine-tune:

  1. The item path at the top of the draft script,
  2. which of the two effect functions (tag or delete) to apply to the first match (if there is one), and
  3. whether or not to uncomment the lines (73, 74) which save the file.

( The effects should prove reversible with ⌘Z )

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

    // Use with dummy data only – purely illustrative
    // Ver 0.2

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

    const itemPath = "//(@now and @type=note)[0]";

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

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

        // -------------- POSSIBLE EFFECTS ---------------

        const effectZapTag = item => (
            item.setAttribute("data-zap", ""),
            `Tagged with @zap :: '${item.bodyString}'`
        );


        const effectDeleteItem = item => {
            const bodyString = item.bodyString;

            return (
                outline.removeItems(item),
                `Deleted item :: '${bodyString}'`
            );
        };

        // ------- APPLIED TO FIRST MATCH (IF ANY) -------
        const applyEffect = effect =>
            item => {
                let message = "";

                outline.groupUndoAndChanges(() => {
                    message = effect(item);
                });

                return message;
            };

        return 0 < matches.length
            ? applyEffect(
                // ---------- SPECIFIED EFFECT -----------
                effectZapTag
                // effectDeleteItem
            )(
                matches[0]
            )
            : `No matches for "${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 }
                })
                // -------- UNCOMMENT WHEN READY ---------
                // , doc.save()
                // , `'${doc.name()}' saved.`
            )
            : "No document open in TaskPaper.";
    };

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

Brilliant. Thank you @complexpoint !

Tests on a backup copy worked as expected. ⌘z works too.

Will run run later today on the main TaskPaper file, and report back if any issues arise.

Cheers!

1 Like

Worked flawlessly 10 times today. :grinning:

2 Likes