TaskPaper 3 :: Toggle between one comma-delimited line and a series of lines Macro

TaskPaper 3 :: Toggle between one comma-delimited line and a series of lines Macro (v9.0d4)

horiz vert
TaskPaper 3 -- Toggle between one comma-delimited line and a series of lines.kmmacros (23 KB)

JS source

(() => {
    'use strict';

    // Toggle <--> between a single (selected) comma-delimited line
    // and a series of sibling lines without commas:

    // alpha, beta, gamma, delta
    // <-->
    // alpha
    // beta
    // gamma
    // delta

    // Rob Trew 2019
    // Ver 0.01

    // JS FOR AUTOMATION CONTEXT --------------------------
    const main = () => {

        // TASKPAPER JS CONTEXT ---------------------------
        const tp3Context = (editor, options) => {
            const inner = () => {
                const
                    rgxCommas = /,\s*/g,
                    item = editor.selection.startItem,
                    locn = editor.getLocationForItemOffset(item, 0),
                    itemText = item.bodyContentString,
                    outline = editor.outline;

                return rgxCommas.test(itemText) ? (() => {
                    const xs = itemText.split(rgxCommas);
                    return (
                        // Effects
                        outline.groupUndoAndChanges(() => {
                            item.parent.insertChildrenBefore(
                                xs.slice(1).map(
                                    s => outline.createItem(s)
                                ),
                                item.nextSibling
                            );
                            item.bodyContentString = xs[0];
                        }),
                        editor.moveSelectionToRange(locn, locn),

                        // Value
                        xs.join('\n')
                    );
                })() : (() => {
                    const followingLeafSiblings = (
                        until(
                            xs => {
                                const
                                    h = xs[0],
                                    none = null === h,
                                    txt = none ? '' : (
                                        h.bodyContentString.trim()
                                    );
                                return none || h.hasChildren || (
                                    1 > txt.length
                                ) || txt.includes(',');
                            },
                            xs => [xs[0].nextSibling].concat(xs),
                            [item.nextSibling]
                        ).slice(1)
                    );
                    return 0 < followingLeafSiblings.length ? (

                        // Effects
                        outline.groupUndoAndChanges(() => (
                            item.bodyContentString = [
                                item
                            ]
                            .concat(reverse(followingLeafSiblings))
                            .map(x => x.bodyContentString).join(', '),
                            outline.removeItems(followingLeafSiblings)
                        )),
                        editor.moveSelectionToRange(locn, locn),

                        // Value
                        item.bodyContentString
                    ) : '';
                })()
            };

            // GENERAL FUNCTIONS FOR TASKPAPER JS CONTEXT--

            // until :: (a -> Bool) -> (a -> a) -> a -> a
            const until = (p, f, x) => {
                let v = x;
                while (!p(v)) v = f(v);
                return v;
            };

            // reverse :: [a] -> [a]
            const reverse = xs =>
                xs.slice(0).reverse();

            return inner();
        };

        const ds = Application('TaskPaper').documents;
        return bindLR(
            0 < ds.length ? (
                Right(ds.at(0))
            ) : Left('No documents open in TaskPaper'),
            d => d.evaluate({
                script: tp3Context.toString(),
                withOptions: {
                    optionName: 'someValue'
                }
            })
        );
    };

    // GENERAL FUNCTIONS FOR JS FOR AUTOMATION CONTEXT ----
    // https://github.com/RobTrew/prelude-jxa

    // Left :: a -> Either a b
    const Left = x => ({
        type: 'Either',
        Left: x
    });

    // Right :: b -> Either a b
    const Right = x => ({
        type: 'Either',
        Right: x
    });

    // bindLR (>>=) :: Either a -> (a -> Either b) -> Either b
    const bindLR = (m, mf) =>
        undefined !== m.Left ? (
            m
        ) : mf(m.Right);

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