"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.printers = exports.parsers = exports.languages = exports.options = void 0;
const prettier_1 = require("prettier");
const doc_1 = require("prettier/doc");
const parser_html_1 = require("prettier/parser-html");
const parse_1 = require("./parse");
const htmlParser = parser_html_1.parsers.html;
const PLUGIN_KEY = "go-template";
exports.options = {
    goTemplateBracketSpacing: {
        type: "boolean",
        category: "Global",
        description: "Specifies whether the brackets should have spacing around the statement.",
        default: true,
    },
};
exports.languages = [
    {
        name: "GoTemplate",
        parsers: [PLUGIN_KEY],
        extensions: [
            ".go.html",
            ".gohtml",
            ".gotmpl",
            ".go.tmpl",
            ".tmpl",
            ".tpl",
            ".html.tmpl",
            ".html.tpl",
        ],
        vscodeLanguageIds: ["gotemplate", "gohtml", "GoTemplate", "GoHTML"],
    },
];
exports.parsers = {
    [PLUGIN_KEY]: {
        astFormat: PLUGIN_KEY,
        preprocess: (text) => text.endsWith("\n") ? text.slice(0, text.length - 1) : text,
        parse: parse_1.parseGoTemplate,
        locStart: (node) => node.index,
        locEnd: (node) => node.index + node.length,
    },
};
exports.printers = {
    [PLUGIN_KEY]: {
        print: (path, options, print) => {
            const node = path.getNode();
            switch (node === null || node === void 0 ? void 0 : node.type) {
                case "inline":
                    return printInline(node, path, options, print);
                case "double-block":
                    return printMultiBlock(node, path, print);
                case "unformattable":
                    return printUnformattable(node, options);
            }
            throw new Error(`An error occured during printing. Found invalid node ${node === null || node === void 0 ? void 0 : node.type}.`);
        },
        embed: (path, print, textToDoc, options) => {
            try {
                return embed(path, print, textToDoc, options);
            }
            catch (e) {
                console.error("Formatting failed.", e);
                throw e;
            }
        },
    },
};
const embed = (path, print, textToDoc, options) => {
    const node = path.getNode();
    if (!node) {
        return null;
    }
    if (hasPrettierIgnoreLine(node)) {
        return options.originalText.substring(options.locStart(node), options.locEnd(node));
    }
    if (node.type !== "block" && node.type !== "root") {
        return null;
    }
    const html = textToDoc(node.aliasedContent, Object.assign(Object.assign({}, options), { parser: "html", parentParser: "go-template" }));
    const mapped = doc_1.utils.stripTrailingHardline(doc_1.utils.mapDoc(html, (currentDoc) => {
        if (typeof currentDoc !== "string") {
            return currentDoc;
        }
        let result = currentDoc;
        Object.keys(node.children).forEach((key) => (result = prettier_1.doc.utils.mapDoc(result, (docNode) => typeof docNode !== "string" || !docNode.includes(key)
            ? docNode
            : doc_1.builders.concat([
                docNode.substring(0, docNode.indexOf(key)),
                path.call(print, "children", key),
                docNode.substring(docNode.indexOf(key) + key.length),
            ]))));
        return result;
    }));
    if ((0, parse_1.isRoot)(node)) {
        return doc_1.builders.concat([mapped, doc_1.builders.hardline]);
    }
    const startStatement = path.call(print, "start");
    const endStatement = node.end ? path.call(print, "end") : "";
    if (isPrettierIgnoreBlock(node)) {
        return doc_1.builders.concat([
            doc_1.utils.removeLines(path.call(print, "start")),
            printPlainBlock(node.content),
            endStatement,
        ]);
    }
    const content = node.aliasedContent.trim()
        ? doc_1.builders.indent(doc_1.builders.concat([doc_1.builders.softline, mapped]))
        : "";
    const result = doc_1.builders.concat([
        startStatement,
        content,
        doc_1.builders.softline,
        endStatement,
    ]);
    const emptyLine = !!node.end && isFollowedByEmptyLine(node.end, options.originalText)
        ? doc_1.builders.softline
        : "";
    if ((0, parse_1.isMultiBlock)(node.parent)) {
        return doc_1.builders.concat([result, emptyLine]);
    }
    return doc_1.builders.group(doc_1.builders.concat([doc_1.builders.group(result), emptyLine]), {
        shouldBreak: !!node.end && hasNodeLinebreak(node.end, options.originalText),
    });
};
function printMultiBlock(node, path, print) {
    return doc_1.builders.concat([...path.map(print, "blocks")]);
}
function isFollowedByNode(node) {
    const parent = getFirstBlockParent(node).parent;
    const start = parent.aliasedContent.indexOf(node.id) + node.id.length;
    let nextNodeIndex = -1;
    Object.keys(parent.children).forEach((key) => {
        const index = parent.aliasedContent.indexOf(key, start);
        if (nextNodeIndex == -1 || index < nextNodeIndex) {
            nextNodeIndex = index;
        }
    });
    return !!parent.aliasedContent
        .substring(start, nextNodeIndex)
        .match(/^\s+$/m);
}
function printInline(node, path, options, print) {
    const isBlockNode = isBlockEnd(node) || isBlockStart(node);
    const emptyLine = isFollowedByEmptyLine(node, options.originalText) && isFollowedByNode(node)
        ? doc_1.builders.softline
        : "";
    const result = [
        printStatement(node.statement, options.goTemplateBracketSpacing, {
            start: node.startDelimiter,
            end: node.endDelimiter,
        }),
    ];
    return doc_1.builders.group(doc_1.builders.concat([...result, emptyLine]), {
        shouldBreak: hasNodeLinebreak(node, options.originalText) && !isBlockNode,
    });
}
function isBlockEnd(node) {
    const { parent } = getFirstBlockParent(node);
    return (0, parse_1.isBlock)(parent) && parent.end === node;
}
function isBlockStart(node) {
    const { parent } = getFirstBlockParent(node);
    return (0, parse_1.isBlock)(parent) && parent.start === node;
}
function printStatement(statement, addSpaces, delimiter = {
    start: "",
    end: "",
}) {
    const space = addSpaces ? " " : "";
    const shouldBreak = statement.includes("\n");
    const content = shouldBreak
        ? statement
            .trim()
            .split("\n")
            .map((line, _, array) => array.indexOf(line) === array.length - 1
            ? doc_1.builders.concat([line.trim(), doc_1.builders.softline])
            : doc_1.builders.indent(doc_1.builders.concat([line.trim(), doc_1.builders.softline])))
        : [statement.trim()];
    return doc_1.builders.group(doc_1.builders.concat([
        "{{",
        delimiter.start,
        space,
        ...content,
        shouldBreak ? "" : space,
        delimiter.end,
        "}}",
    ]), { shouldBreak });
}
function hasPrettierIgnoreLine(node) {
    if ((0, parse_1.isRoot)(node)) {
        return false;
    }
    const { parent, child } = getFirstBlockParent(node);
    const regex = new RegExp(`(?:<!--|{{).*?prettier-ignore.*?(?:-->|}})\n.*${child.id}`);
    return !!parent.aliasedContent.match(regex);
}
function isPrettierIgnoreBlock(node) {
    return (0, parse_1.isBlock)(node) && node.keyword === "prettier-ignore-start";
}
function hasNodeLinebreak(node, source) {
    const start = node.index + node.length;
    const end = source.indexOf("\n", start);
    const suffix = source.substring(start, end);
    return !suffix;
}
function isFollowedByEmptyLine(node, source) {
    const start = node.index + node.length;
    const firstLineBreak = source.indexOf("\n", start);
    const secondLineBreak = source.indexOf("\n", firstLineBreak + 1);
    const emptyLine = source
        .substring(firstLineBreak + 1, secondLineBreak)
        .trim();
    const isLastNode = !!source.substring(start).match(/^\s*$/);
    return (firstLineBreak !== -1 && secondLineBreak !== -1 && !emptyLine && !isLastNode);
}
function getFirstBlockParent(node) {
    let previous = node;
    let current = node.parent;
    while (!(0, parse_1.isBlock)(current) && !(0, parse_1.isRoot)(current)) {
        previous = current;
        current = current.parent;
    }
    return {
        child: previous,
        parent: current,
    };
}
function printUnformattable(node, options) {
    var _a, _b;
    const start = options.originalText.lastIndexOf("\n", node.index - 1);
    const line = options.originalText.substring(start, node.index + node.length);
    const lineWithoutAdditionalContent = (_b = (_a = line.replace(node.content, "").match(/\s*$/)) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : "";
    return printPlainBlock(lineWithoutAdditionalContent + node.content, false);
}
function printPlainBlock(text, hardlines = true) {
    const isTextEmpty = (input) => !!input.match(/^\s*$/);
    const lines = text.split("\n");
    const segments = lines.filter((value, i) => !(i == 0 || i == lines.length - 1) || !isTextEmpty(value));
    return doc_1.builders.concat([
        ...segments.map((content, i) => doc_1.builders.concat([
            hardlines || i ? doc_1.builders.hardline : "",
            doc_1.builders.trim,
            content,
        ])),
        hardlines ? doc_1.builders.hardline : "",
    ]);
}
//# sourceMappingURL=data:application/json;base64,[encoded data, 26664 characters]