"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getOutline = void 0;
const graphql_1 = require("graphql");
const utils_1 = require("../utils");
const { INLINE_FRAGMENT } = graphql_1.Kind;
const OUTLINEABLE_KINDS = {
    Field: true,
    OperationDefinition: true,
    Document: true,
    SelectionSet: true,
    Name: true,
    FragmentDefinition: true,
    FragmentSpread: true,
    InlineFragment: true,
    ObjectTypeDefinition: true,
    InputObjectTypeDefinition: true,
    InterfaceTypeDefinition: true,
    EnumTypeDefinition: true,
    EnumValueDefinition: true,
    InputValueDefinition: true,
    FieldDefinition: true,
};
function getOutline(documentText) {
    let ast;
    try {
        ast = (0, graphql_1.parse)(documentText);
    }
    catch (_a) {
        return null;
    }
    const visitorFns = outlineTreeConverter(documentText);
    const outlineTrees = (0, graphql_1.visit)(ast, {
        leave(node) {
            if (visitorFns !== undefined && node.kind in visitorFns) {
                return visitorFns[node.kind](node);
            }
            return null;
        },
    });
    return { outlineTrees };
}
exports.getOutline = getOutline;
function outlineTreeConverter(docText) {
    const meta = (node) => {
        return {
            representativeName: node.name,
            startPosition: (0, utils_1.offsetToPosition)(docText, node.loc.start),
            endPosition: (0, utils_1.offsetToPosition)(docText, node.loc.end),
            kind: node.kind,
            children: node.selectionSet || node.fields || node.values || node.arguments || [],
        };
    };
    return {
        Field(node) {
            const tokenizedText = node.alias
                ? [buildToken('plain', node.alias), buildToken('plain', ': ')]
                : [];
            tokenizedText.push(buildToken('plain', node.name));
            return Object.assign({ tokenizedText }, meta(node));
        },
        OperationDefinition: (node) => (Object.assign({ tokenizedText: [
                buildToken('keyword', node.operation),
                buildToken('whitespace', ' '),
                buildToken('class-name', node.name),
            ] }, meta(node))),
        Document: (node) => node.definitions,
        SelectionSet: (node) => concatMap(node.selections, (child) => {
            return child.kind === INLINE_FRAGMENT ? child.selectionSet : child;
        }),
        Name: (node) => node.value,
        FragmentDefinition: (node) => (Object.assign({ tokenizedText: [
                buildToken('keyword', 'fragment'),
                buildToken('whitespace', ' '),
                buildToken('class-name', node.name),
            ] }, meta(node))),
        InterfaceTypeDefinition: (node) => (Object.assign({ tokenizedText: [
                buildToken('keyword', 'interface'),
                buildToken('whitespace', ' '),
                buildToken('class-name', node.name),
            ] }, meta(node))),
        EnumTypeDefinition: (node) => (Object.assign({ tokenizedText: [
                buildToken('keyword', 'enum'),
                buildToken('whitespace', ' '),
                buildToken('class-name', node.name),
            ] }, meta(node))),
        EnumValueDefinition: (node) => (Object.assign({ tokenizedText: [buildToken('plain', node.name)] }, meta(node))),
        ObjectTypeDefinition: (node) => (Object.assign({ tokenizedText: [
                buildToken('keyword', 'type'),
                buildToken('whitespace', ' '),
                buildToken('class-name', node.name),
            ] }, meta(node))),
        InputObjectTypeDefinition: (node) => (Object.assign({ tokenizedText: [
                buildToken('keyword', 'input'),
                buildToken('whitespace', ' '),
                buildToken('class-name', node.name),
            ] }, meta(node))),
        FragmentSpread: (node) => (Object.assign({ tokenizedText: [
                buildToken('plain', '...'),
                buildToken('class-name', node.name),
            ] }, meta(node))),
        InputValueDefinition(node) {
            return Object.assign({ tokenizedText: [buildToken('plain', node.name)] }, meta(node));
        },
        FieldDefinition(node) {
            return Object.assign({ tokenizedText: [buildToken('plain', node.name)] }, meta(node));
        },
        InlineFragment: (node) => node.selectionSet,
    };
}
function buildToken(kind, value) {
    return { kind, value };
}
function concatMap(arr, fn) {
    const res = [];
    for (let i = 0; i < arr.length; i++) {
        const x = fn(arr[i], i);
        if (Array.isArray(x)) {
            res.push(...x);
        }
        else {
            res.push(x);
        }
    }
    return res;
}
//# sourceMappingURL=getOutline.js.map