Getting all descendants of selected items


#1

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?


#2

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.


#3

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
    )

})();