BIKE Outliner – Toggle numbering of selected rows

A macro for Jesse Grosjean's Bike Outliner.

BIKE – Toggle numbering of selected rows.kmmacros (25 KB)


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

    // Toggle numbering of selected rows.
    // Adds or clears simple numbering for a
    // sequence of selected peers.

    // Notes:
    //   1. Descendants of the selected rows are ignored.
    //        (for example this line would be unchanged)
    //   2. Blank lines are ignored.

    // Rob Trew @2022
    //
    // Ver 0.03

    // -------- TOGGLE NUMBERING OF SELECTED ROWS --------
    // main :: IO ()
    const main = () => {
        const
            doc = Application("Bike").documents.at(0);

        return doc.exists() ? (() => {
            const
                inSelection = selected(),
                rows = doc.rows.where(inSelection),
                minIndent = Math.min(...rows.level()),
                topPeers = rows.where({
                    _and: [
                        minimumLevel(minIndent),
                        ...inSelection._and
                    ]
                });

            return numbersCleared(topPeers) ? (
                "Numbering cleared."
            ) : numberedPeers(topPeers);
        })() : "No documents open in Bike.";
    };


    // numbersCleared :: Bike Rows -> IO Bool
    const numbersCleared = peerRows => {
        const rgxNumbering = /^\s*\d+\. /u;

        return 0 < peerRows().flatMap(x => {
            const s = x.name();

            return rgxNumbering.test(s) ? [(
                x.name = s.replace(
                    rgxNumbering, ""
                ),
                1
            )] : [];
        }).length;
    };


    // numberedPeers :: Bike Rows -> IO String
    const numberedPeers = peerRows => {
        const nDigits = (`${peerRows.length}`).length;

        return (
            zipWith(
                row => k => row.name = k
            )(peerRows())(
                peerRows.name().map((s, i) => {
                    const
                        prefix = `${1 + i}`
                        .padStart(nDigits, " ");

                    return `${prefix}. ${s}`;
                })
            ),
            `Numbered up to ${peerRows.length}.`
        );
    };

    // selected :: () -> JXA Condition
    const selected = () => ({
        _and: [
            {selected: true},
            {_not: [{
                name: ""
            }]}
        ]
    });


    // minimumLevel :: n -> JXA Condition
    const minimumLevel = n => ({
        level: {
            _lessThanEquals: n
        }
    });


    // --------------------- GENERIC ---------------------

    // zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
    const zipWith = f =>
    // A list constructed by zipping with a
    // custom function, rather than with the
    // default tuple constructor.
        xs => ys => xs.map(
            (x, i) => f(x)(ys[i])
        ).slice(
            0, Math.min(xs.length, ys.length)
        );


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


Other Keyboard Maestro macros for BIKE Outliner

3 Likes