How to check for overlay trees?

I’m interested in (iterating over) overlay trees. However, I’m not sure how I should check for the existence of a overlay tree:

  1. Is there a way to (performantly) check for the existence of an overlay tree?
  2. Is there a method that will enter overlay trees (i.e. similar to resolveInner()), that will enter the isTop node directly (currently I have to traverse up to find it)?
  3. Is there a way to check whether resolveInner() entered a overlay tree?
  4. Is there a method, like tree.iterate(), that will iterate all sub-trees as well?

For Markdown, I am currently able to deduce the existence of an overlay tree, because they’re likely to start at the same position as the first CodeText node coming after a CodeInfo node:

const state = view.state;
const tree = syntaxTree(state);
const doc = state.doc;

tree.iterate({
    enter: (nodeRef) => {
        if (nodeRef.name !== 'CodeInfo') return;

        const codeText = nodeRef.node.nextSibling;

        if (codeText?.name !== 'CodeText') return;

        // Unsure how to deduce whether resolveInner() resolved to a sub tree or not.
        const subTreeNode = tree.resolveInner(codeText.from, 1);

        const subTreeCursor = subTreeNode.cursor();

        // May it be possible to enter the top node directly?
        while (!subTreeCursor.type.isTop && subTreeCursor.parent()) {}

        if (subTreeCursor.from === 0) return; // Hit top `Document` node; there wasn't a overlay tree after all.

        console.log(subTreeCursor.name); // E.g. `Script` for javascript
        console.log(doc.sliceString(subTreeCursor.from, subTreeCursor.to)); // Full javascript snippet.

        iterateSubTree(subTreeCursor, (cursor, depth) => {
            console.log('\t'.repeat(depth) + cursor.name); // Javascript nodes.
        });
    },
});

The presence of overlays in their descendant nodes isn’t stored directly in nodes, so there’s no way to check for this without going through the tree. You can be sure a parser that doesn’t have wrappers won’t have nested trees, but you cannot be sure that one that does allow them actually has them, from that flag. You could iterate the tree with the ExcludeBuffers iteration option to make the iteration cheaper though (since subtrees can only be stored on nodes that are allocated as objects, not ones stored in buffer leaf nodes).

No, no such method exists (and if it did, it’d still have to scan down to the actual innermost node to make sure no further subtrees cover it). topNodeAt in src/language.ts in @codemirror/language handles a situation similar to what you are asking for here.

Not directly. You’d have to follow parent pointers up to the nearest top node and compare it to the root tree.

No. Because overlays make the tree no longer strictly tree-shaped (there’s no clear ordering between the overlay tree and the parts of the parent tree it covers), iteration skips them, unless you add your own logic that checks for them.

1 Like