function getElementHierarchy()

in src/utils/lexerUtils.ts [70:146]


function getElementHierarchy(text: string, tokens: number[][], tagOrOffset: number | string): ElementNode | ElementNode[] | undefined {
    let targetTag: string | undefined;
    let cursorOffset: number | undefined;
    if (typeof tagOrOffset === "string") {
        targetTag = tagOrOffset;
    } else if (typeof tagOrOffset === "number") {
        cursorOffset = tagOrOffset;
    }
    const n: number = tokens.length;
    const elementNodes: ElementNode[] = [];
    const tagNodes: ElementNode[] = [];
    let cursorNode: ElementNode | undefined;
    let pointer: number = 0;
    let i: number = 0;

    while (i < n) {
        const token: number[] = tokens[i];
        const currentNode: ElementNode = elementNodes[elementNodes.length - 1];
        switch (token[0]) {
            case NodeTypes.XML_DECLARATION:
            case NodeTypes.ELEMENT_NODE: {
                // [_type, start, end] = token;
                const [start, end] = token.slice(1, 3);
                const newElement: ElementNode = new ElementNode(currentNode, text.substring(start, end));
                if (currentNode !== undefined) {
                    currentNode.addChild(newElement);
                }

                pointer = end + 1; // pass ">" mark.
                elementNodes.push(newElement);
                newElement.contentStart = pointer;
                break;
            }
            case NodeTypes.ATTRIBUTE_NODE: {
                // [_type, _keyStart, _keyEnd, _valueStart, valueEnd] = token;
                const valueEnd: number = token[4];
                // Attributes not handled yet.
                pointer = valueEnd + 1; // pass ">" mark.
                currentNode.contentStart = pointer;
                break;
            }
            case NodeTypes.TEXT_NODE: {
                // [_type, start, end] = token;
                const [start, end] = token.slice(1, 3);
                if (currentNode !== undefined) {
                    currentNode.text = text.substring(start, end);
                }
                pointer = end;
                break;
            }
            case NodeTypes.CLOSE_ELEMENT: {
                currentNode.contentEnd = pointer;
                elementNodes.pop();
                break;
            }
            default:
                break;
        }
        if (targetTag !== undefined && currentNode !== undefined && targetTag === currentNode.tag && tagNodes.indexOf(currentNode) < 0) {
            tagNodes.push(currentNode);
        }
        if (cursorOffset !== undefined
            && cursorNode === undefined
            && currentNode !== undefined
            && currentNode.contentStart !== undefined && currentNode.contentStart <= cursorOffset
            && currentNode.contentEnd !== undefined && cursorOffset <= currentNode.contentEnd) {
            cursorNode = currentNode;
        }
        i += 1;
    }
    if (targetTag !== undefined) {
        return tagNodes;
    } else if (cursorOffset !== undefined) {
        return cursorNode !== undefined ? cursorNode : elementNodes[elementNodes.length - 1];
    }
    return undefined;
}