"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.baseParse = void 0;
const muggle_string_1 = require("muggle-string");
const source_map_1 = require("@volar/source-map");
const pug_lexer_1 = __importDefault(require("pug-lexer"));
const vscode_languageserver_textdocument_1 = require("vscode-languageserver-textdocument");
const pugParser = require('pug-parser');
function baseParse(pugCode) {
    const fileName = 'foo.pug';
    const pugTextDocument = vscode_languageserver_textdocument_1.TextDocument.create('file:///a.pug', 'jade', 0, pugCode);
    const codes = [];
    let error;
    let emptyLineEnds = [];
    let attrsBlocks;
    let ast;
    try {
        const tokens = (0, pug_lexer_1.default)(pugCode, { filename: fileName });
        emptyLineEnds = collectEmptyLineEnds(tokens);
        attrsBlocks = collectAttrsBlocks(tokens);
        ast = pugParser(tokens, { filename: fileName, src: pugCode });
        visitNode(ast, undefined, undefined);
        codes.push([
            '',
            undefined,
            pugCode.trimEnd().length,
        ]);
    }
    catch (e) {
        const _error = e;
        error = {
            ..._error,
            line: _error.line - 1,
            column: _error.column - 1,
        };
    }
    ;
    return {
        htmlCode: (0, muggle_string_1.toString)(codes),
        mappings: (0, source_map_1.buildMappings)(codes),
        pugTextDocument,
        error,
        ast,
        emptyLineEnds,
    };
    function visitNode(node, next, parent) {
        if (node.type === 'Block') {
            for (let i = 0; i < node.nodes.length; i++) {
                visitNode(node.nodes[i], node.nodes[i + 1], node);
            }
        }
        else if (node.type === 'Mixin') {
            if (node.block) {
                visitNode(node.block, undefined, node);
            }
        }
        else if (node.type === 'Tag') {
            const pugTagRange = getDocRange(node.line, node.column, node.name.length);
            codes.push([
                '',
                undefined,
                pugTagRange.start,
            ]);
            const selfClosing = node.block.nodes.length === 0;
            addStartTag(node, selfClosing);
            if (!selfClosing) {
                visitNode(node.block, next, parent);
                addEndTag(node, next, parent);
            }
            codes.push([
                '',
                undefined,
                pugTagRange.start,
            ]);
        }
        else if (node.type === 'Text') {
            codes.push([
                node.val,
                undefined,
                getDocOffset(node.line, node.column),
            ]);
        }
    }
    function addStartTag(node, selfClosing) {
        codes.push([
            '',
            undefined,
            getDocOffset(node.line, node.column),
        ]);
        codes.push('<');
        const tagRange = getDocRange(node.line, node.column, node.name.length);
        if (pugCode.substring(tagRange.start, tagRange.end) === node.name) {
            codes.push([
                node.name,
                undefined,
                tagRange.start,
            ]);
        }
        else {
            codes.push(node.name);
        }
        const noTitleAttrs = node.attrs.filter(attr => !attr.mustEscape && attr.name !== 'class');
        const noTitleClassAttrs = node.attrs.filter(attr => !attr.mustEscape && attr.name === 'class');
        const attrsBlock = attrsBlocks.get(getDocOffset(node.line, node.column)); // support attr auto-complete in spaces
        const hasClassAttr = attrsBlock && attrsBlock.text.match(/\bclass\b\s*=/i);
        if (!hasClassAttr) {
            addClassesOrStyles(noTitleClassAttrs, 'class');
        }
        for (const attr of noTitleAttrs) {
            codes.push(' ');
            codes.push(attr.name);
            if (typeof attr.val !== 'boolean') {
                codes.push('=');
                codes.push([
                    attr.val,
                    undefined,
                    getDocOffset(attr.line, attr.column),
                ]);
            }
        }
        if (attrsBlock) {
            codes.push(' ');
            codes.push([
                attrsBlock.text,
                undefined,
                attrsBlock.offset,
            ]);
        }
        if (selfClosing) {
            codes.push(' />');
        }
        else {
            codes.push('>');
        }
    }
    function addEndTag(node, next, parent) {
        let nextStart;
        if (next) {
            if (next.type === 'Block') {
                nextStart = getDocOffset(next.line, 1);
            }
            else {
                nextStart = getDocOffset(next.line, next.column);
            }
        }
        else if (!parent) {
            nextStart = pugCode.length;
        }
        if (nextStart !== undefined) {
            codes.push([
                '',
                undefined,
                nextStart,
            ]);
        }
        codes.push(`</${node.name}>`);
    }
    function addClassesOrStyles(attrs, attrName) {
        if (!attrs.length)
            return;
        codes.push(' ');
        codes.push(attrName);
        codes.push('=');
        codes.push('"');
        for (const attr of attrs) {
            if (typeof attr.val !== 'boolean') {
                codes.push(' ');
                codes.push([
                    attr.val.slice(1, -1),
                    undefined,
                    getDocOffset(attr.line, attr.column + 1),
                ]);
            }
        }
        codes.push('"');
    }
    function collectEmptyLineEnds(tokens) {
        const ends = [];
        for (const token of tokens) {
            if (token.type === 'newline' || token.type === 'outdent') {
                let currentLine = token.loc.start.line - 2;
                let prevLine = getLineText(pugTextDocument, currentLine);
                while (prevLine.trim() === '') {
                    ends.push(pugTextDocument.offsetAt({ line: currentLine + 1, character: 0 }) - 1);
                    if (currentLine <= 0)
                        break;
                    currentLine--;
                    prevLine = getLineText(pugTextDocument, currentLine);
                }
            }
        }
        return ends.sort((a, b) => a - b);
    }
    function collectAttrsBlocks(tokens) {
        const blocks = new Map();
        for (let i = 0; i < tokens.length; i++) {
            const token = tokens[i];
            if (token.type === 'start-attributes') {
                let tagStart = token;
                for (let j = i - 1; j >= 0; j--) {
                    const prevToken = tokens[j];
                    if (prevToken.type === 'newline'
                        || prevToken.type === 'indent'
                        || prevToken.type === 'outdent'
                        || prevToken.type === ':')
                        break;
                    tagStart = prevToken;
                    if (prevToken.type === 'tag')
                        break;
                }
                let prevToken = token;
                let text = '';
                for (i++; i < tokens.length; i++) {
                    const attrToken = tokens[i];
                    addPrevSpace(attrToken);
                    if (attrToken.type === 'attribute') {
                        let attrText = pugCode.substring(getDocOffset(attrToken.loc.start.line, attrToken.loc.start.column), getDocOffset(attrToken.loc.end.line, attrToken.loc.end.column));
                        if (typeof attrToken.val === 'string' && attrText.indexOf('=') >= 0) {
                            let valText = attrToken.val;
                            if (valText.startsWith('`') && valText.endsWith('`')) {
                                valText = `"${valText.slice(1, -1)}"`;
                            }
                            valText = valText.replace(/ \\\n/g, '//\n');
                            text += attrText.substring(0, attrText.lastIndexOf(attrToken.val)) + valText;
                        }
                        else {
                            text += attrText;
                        }
                    }
                    else if (attrToken.type === 'end-attributes') {
                        blocks.set(getDocOffset(tagStart.loc.start.line, tagStart.loc.start.column), {
                            offset: getDocOffset(token.loc.end.line, token.loc.end.column),
                            text,
                        });
                        break;
                    }
                    prevToken = attrToken;
                }
                function addPrevSpace(currentToken) {
                    text += pugCode.substring(getDocOffset(prevToken.loc.end.line, prevToken.loc.end.column), getDocOffset(currentToken.loc.start.line, currentToken.loc.start.column)).replace(/,/g, '\n');
                }
            }
        }
        return blocks;
    }
    function getDocOffset(pugLine, pugColumn) {
        return pugTextDocument.offsetAt({ line: pugLine - 1, character: pugColumn - 1 });
    }
    function getDocRange(pugLine, pugColumn, length) {
        const start = getDocOffset(pugLine, pugColumn);
        const end = start + length;
        return {
            start,
            end,
        };
    }
}
exports.baseParse = baseParse;
function getLineText(document, line) {
    const endOffset = document.offsetAt({ line: line + 1, character: 0 });
    const end = document.positionAt(endOffset);
    const text = document.getText({
        start: { line: line, character: 0 },
        end: end.line === line ? end : document.positionAt(endOffset - 1),
    });
    return text;
}
//# sourceMappingURL=baseParse.js.map