Getting all descendants of selected items

What’s an idomatic way for my omnijs plugin to build an array of all selected items and their descendants?

Right now, I’m doing something ugly and inefficient:

I iterate through all selected items, pushing each and all of its descendants into a new array, then dispose of all the duplicates in the new array when I’m done.

My way works, but there’s got to be a tidy way to do this that’s escaping me.

And to be clear, I don’t want to force the application to visibly expand anything–I just want the data.

Does anyone have a way?

I think you should be able to get the top level items of the selection:

// topSelections :: IO () -> [OO Item]
const topSelections = () => {
    const
        d = document,
        o = d.outline;
    return o.itemsSortedByPosition(
        o.topItems(
            d.editors[0].selection.items
        )
    );
};

and then collect and concatenate their descendant lists, which will be distinct.

1 Like

Expanding a little, and calling some omniJS code from JXA, assuming an active OO window:

(() => {
    'use strict';

    // OmniJS context -------------------------------------

    // selectedNodesDescendantCount :: Int
    const selectedNodesDescendantCount = () => {

        const main = () => {
            const xs = topSelections();
            return concat(xs.map(x => x.descendants)).length
        };

        // concat :: [[a]] -> [a]
        // concat :: [String] -> String
        const concat = xs =>
            0 < xs.length ? (() => {
                const unit = 'string' !== typeof xs[0] ? (
                    []
                ) : '';
                return unit.concat.apply(unit, xs);
            })() : [];

        // topSelections :: IO () -> [OO Item]
        const topSelections = () => {
            const
                d = document,
                o = d.outline;
            return o.itemsSortedByPosition(
                o.topItems(
                    d.editors[0].selection.items
                )
            );
        };

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

    // JXA context ----------------------------------------

    // outlineOmniJSWithArgs :: Function -> [...OptionalArgs] -> a
    function outlineOmniJSWithArgs(f) {
        return Application('omniOutliner')
            .evaluateJavascript(
                `(${f})(${Array.from(arguments)
                .slice(1).map(JSON.stringify)})`
            );
    };

    return outlineOmniJSWithArgs(
        selectedNodesDescendantCount
    )

})();

1 Like