"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.transformDocumentLinkTarget = transformDocumentLinkTarget;
exports.transformMarkdown = transformMarkdown;
exports.transformCompletionItem = transformCompletionItem;
exports.transformCompletionList = transformCompletionList;
exports.transformDocumentSymbol = transformDocumentSymbol;
exports.transformFoldingRanges = transformFoldingRanges;
exports.transformHover = transformHover;
exports.transformLocation = transformLocation;
exports.transformLocations = transformLocations;
exports.transformSelectionRange = transformSelectionRange;
exports.transformSelectionRanges = transformSelectionRanges;
exports.transformTextEdit = transformTextEdit;
exports.transformWorkspaceSymbol = transformWorkspaceSymbol;
exports.transformWorkspaceEdit = transformWorkspaceEdit;
exports.pushEditToDocumentChanges = pushEditToDocumentChanges;
const language_core_1 = require("@volar/language-core");
const vscode_uri_1 = require("vscode-uri");
const featureWorkers_1 = require("./featureWorkers");
function transformDocumentLinkTarget(_target, context) {
    let target = vscode_uri_1.URI.parse(_target);
    const decoded = context.decodeEmbeddedDocumentUri(target);
    if (!decoded) {
        return target;
    }
    const embeddedRange = target.fragment.match(/^L(\d+)(,(\d+))?(-L(\d+)(,(\d+))?)?$/);
    const sourceScript = context.language.scripts.get(decoded[0]);
    const virtualCode = sourceScript?.generated?.embeddedCodes.get(decoded[1]);
    target = decoded[0];
    if (embeddedRange && sourceScript && virtualCode) {
        const embeddedDocument = context.documents.get(context.encodeEmbeddedDocumentUri(sourceScript.id, virtualCode.id), virtualCode.languageId, virtualCode.snapshot);
        for (const [sourceScript, map] of context.language.maps.forEach(virtualCode)) {
            if (!map.mappings.some(mapping => (0, language_core_1.isDocumentLinkEnabled)(mapping.data))) {
                continue;
            }
            const sourceDocument = context.documents.get(sourceScript.id, sourceScript.languageId, sourceScript.snapshot);
            const docs = [sourceDocument, embeddedDocument, map];
            const startLine = Number(embeddedRange[1]) - 1;
            const startCharacter = Number(embeddedRange[3] ?? 1) - 1;
            if (embeddedRange[5] !== undefined) {
                const endLine = Number(embeddedRange[5]) - 1;
                const endCharacter = Number(embeddedRange[7] ?? 1) - 1;
                const sourceRange = (0, featureWorkers_1.getSourceRange)(docs, {
                    start: { line: startLine, character: startCharacter },
                    end: { line: endLine, character: endCharacter },
                });
                if (sourceRange) {
                    target = target.with({
                        fragment: 'L' + (sourceRange.start.line + 1) + ',' + (sourceRange.start.character + 1)
                            + '-L' + (sourceRange.end.line + 1) + ',' + (sourceRange.end.character + 1),
                    });
                    break;
                }
            }
            else {
                let mapped = false;
                for (const sourcePos of (0, featureWorkers_1.getSourcePositions)(docs, { line: startLine, character: startCharacter })) {
                    mapped = true;
                    target = target.with({
                        fragment: 'L' + (sourcePos.line + 1) + ',' + (sourcePos.character + 1),
                    });
                    break;
                }
                if (mapped) {
                    break;
                }
            }
        }
    }
    return target;
}
function transformMarkdown(content, context) {
    return content.replace(/(?!\()volar-embedded-content:\/\/\w+\/[^)]+/g, match => {
        const segments = match.split('|');
        segments[0] = transformDocumentLinkTarget(segments[0], context).toString();
        return segments.join('|');
    });
}
function transformCompletionItem(item, getOtherRange, document, context) {
    return {
        ...item,
        additionalTextEdits: item.additionalTextEdits
            ?.map(edit => transformTextEdit(edit, getOtherRange, document))
            .filter(edit => !!edit),
        textEdit: item.textEdit
            ? transformTextEdit(item.textEdit, getOtherRange, document)
            : undefined,
        documentation: item.documentation
            ? typeof item.documentation === 'string'
                ? transformMarkdown(item.documentation, context)
                : item.documentation.kind === 'markdown'
                    ? { kind: 'markdown', value: transformMarkdown(item.documentation.value, context) }
                    : item.documentation
            : undefined,
    };
}
function transformCompletionList(completionList, getOtherRange, document, context) {
    return {
        isIncomplete: completionList.isIncomplete,
        itemDefaults: completionList.itemDefaults
            ? {
                ...completionList.itemDefaults,
                editRange: completionList.itemDefaults.editRange
                    ? 'replace' in completionList.itemDefaults.editRange
                        ? {
                            insert: getOtherRange(completionList.itemDefaults.editRange.insert),
                            replace: getOtherRange(completionList.itemDefaults.editRange.replace),
                        }
                        : getOtherRange(completionList.itemDefaults.editRange)
                    : undefined,
            }
            : undefined,
        items: completionList.items.map(item => transformCompletionItem(item, getOtherRange, document, context)),
    };
}
function transformDocumentSymbol(symbol, getOtherRange) {
    const range = getOtherRange(symbol.range);
    if (!range) {
        return;
    }
    const selectionRange = getOtherRange(symbol.selectionRange);
    if (!selectionRange) {
        return;
    }
    return {
        ...symbol,
        range,
        selectionRange,
        children: symbol.children
            ?.map(child => transformDocumentSymbol(child, getOtherRange))
            .filter(child => !!child),
    };
}
function transformFoldingRanges(ranges, getOtherRange) {
    const result = [];
    for (const range of ranges) {
        const otherRange = getOtherRange({
            start: { line: range.startLine, character: range.startCharacter ?? 0 },
            end: { line: range.endLine, character: range.endCharacter ?? 0 },
        });
        if (otherRange) {
            range.startLine = otherRange.start.line;
            range.endLine = otherRange.end.line;
            if (range.startCharacter !== undefined) {
                range.startCharacter = otherRange.start.character;
            }
            if (range.endCharacter !== undefined) {
                range.endCharacter = otherRange.end.character;
            }
            result.push(range);
        }
    }
    return result;
}
function transformHover(hover, getOtherRange) {
    if (!hover?.range) {
        return hover;
    }
    const range = getOtherRange(hover.range);
    if (!range) {
        return;
    }
    return {
        ...hover,
        range,
    };
}
function transformLocation(location, getOtherRange) {
    const range = getOtherRange(location.range);
    if (!range) {
        return;
    }
    return {
        ...location,
        range,
    };
}
function transformLocations(locations, getOtherRange) {
    return locations
        .map(location => transformLocation(location, getOtherRange))
        .filter(location => !!location);
}
function transformSelectionRange(location, getOtherRange) {
    const range = getOtherRange(location.range);
    if (!range) {
        return;
    }
    const parent = location.parent ? transformSelectionRange(location.parent, getOtherRange) : undefined;
    return {
        range,
        parent,
    };
}
function transformSelectionRanges(locations, getOtherRange) {
    return locations
        .map(location => transformSelectionRange(location, getOtherRange))
        .filter(location => !!location);
}
function transformTextEdit(textEdit, getOtherRange, document) {
    if ('range' in textEdit) {
        let range = getOtherRange(textEdit.range);
        if (range) {
            return {
                ...textEdit,
                range,
            };
        }
        const cover = tryRecoverTextEdit(getOtherRange, textEdit.range, textEdit.newText, document);
        if (cover) {
            return {
                ...textEdit,
                range: cover.range,
                newText: cover.newText,
            };
        }
    }
    else if ('replace' in textEdit && 'insert' in textEdit) {
        const insert = getOtherRange(textEdit.insert);
        const replace = insert ? getOtherRange(textEdit.replace) : undefined;
        if (insert && replace) {
            return {
                ...textEdit,
                insert,
                replace,
            };
        }
        const recoverInsert = tryRecoverTextEdit(getOtherRange, textEdit.insert, textEdit.newText, document);
        const recoverReplace = recoverInsert
            ? tryRecoverTextEdit(getOtherRange, textEdit.replace, textEdit.newText, document)
            : undefined;
        if (recoverInsert && recoverReplace && recoverInsert.newText === recoverReplace.newText) {
            return {
                ...textEdit,
                insert: recoverInsert.range,
                replace: recoverReplace.range,
                newText: recoverInsert.newText,
            };
        }
    }
}
/**
 * update edit text from ". foo" to " foo"
 * fix https://github.com/johnsoncodehk/volar/issues/2155
 */
function tryRecoverTextEdit(getOtherRange, replaceRange, newText, document) {
    if (replaceRange.start.line === replaceRange.end.line && replaceRange.end.character > replaceRange.start.character) {
        let character = replaceRange.start.character;
        while (newText.length && replaceRange.end.character > character) {
            const newStart = { line: replaceRange.start.line, character: replaceRange.start.character + 1 };
            if (document.getText({ start: replaceRange.start, end: newStart }) === newText[0]) {
                newText = newText.slice(1);
                character++;
                const otherRange = getOtherRange({ start: newStart, end: replaceRange.end });
                if (otherRange) {
                    return {
                        newText,
                        range: otherRange,
                    };
                }
            }
            else {
                break;
            }
        }
    }
}
function transformWorkspaceSymbol(symbol, getOtherLocation) {
    if (!('range' in symbol.location)) {
        return symbol;
    }
    const loc = getOtherLocation(symbol.location);
    if (!loc) {
        return;
    }
    return {
        ...symbol,
        location: loc,
    };
}
function transformWorkspaceEdit(edit, context, mode, versions = {}) {
    const sourceResult = {};
    let hasResult = false;
    for (const tsUri in edit.changeAnnotations) {
        sourceResult.changeAnnotations ??= {};
        const tsAnno = edit.changeAnnotations[tsUri];
        const decoded = context.decodeEmbeddedDocumentUri(vscode_uri_1.URI.parse(tsUri));
        const sourceScript = decoded && context.language.scripts.get(decoded[0]);
        const virtualCode = decoded && sourceScript?.generated?.embeddedCodes.get(decoded[1]);
        if (sourceScript && virtualCode) {
            for (const [sourceScript] of context.language.maps.forEach(virtualCode)) {
                // TODO: check capability?
                const uri = sourceScript.id.toString();
                sourceResult.changeAnnotations[uri] = tsAnno;
                break;
            }
        }
        else {
            sourceResult.changeAnnotations[tsUri] = tsAnno;
        }
    }
    for (const tsUri in edit.changes) {
        sourceResult.changes ??= {};
        const decoded = context.decodeEmbeddedDocumentUri(vscode_uri_1.URI.parse(tsUri));
        const sourceScript = decoded && context.language.scripts.get(decoded[0]);
        const virtualCode = decoded && sourceScript?.generated?.embeddedCodes.get(decoded[1]);
        if (sourceScript && virtualCode) {
            const embeddedDocument = context.documents.get(context.encodeEmbeddedDocumentUri(sourceScript.id, virtualCode.id), virtualCode.languageId, virtualCode.snapshot);
            for (const [sourceScript, map] of context.language.maps.forEach(virtualCode)) {
                const sourceDocument = context.documents.get(sourceScript.id, sourceScript.languageId, sourceScript.snapshot);
                const docs = [sourceDocument, embeddedDocument, map];
                const tsEdits = edit.changes[tsUri];
                for (const tsEdit of tsEdits) {
                    if (mode === 'rename' || mode === 'fileName' || mode === 'codeAction') {
                        let _data;
                        const range = (0, featureWorkers_1.getSourceRange)(docs, tsEdit.range, data => {
                            _data = data;
                            return (0, language_core_1.isRenameEnabled)(data);
                        });
                        if (range) {
                            sourceResult.changes[sourceDocument.uri] ??= [];
                            sourceResult.changes[sourceDocument.uri].push({
                                newText: (0, language_core_1.resolveRenameEditText)(tsEdit.newText, _data),
                                range,
                            });
                            hasResult = true;
                        }
                    }
                    else {
                        const range = (0, featureWorkers_1.getSourceRange)(docs, tsEdit.range);
                        if (range) {
                            sourceResult.changes[sourceDocument.uri] ??= [];
                            sourceResult.changes[sourceDocument.uri].push({ newText: tsEdit.newText, range });
                            hasResult = true;
                        }
                    }
                }
            }
        }
        else {
            sourceResult.changes[tsUri] = edit.changes[tsUri];
            hasResult = true;
        }
    }
    if (edit.documentChanges) {
        for (const tsDocEdit of edit.documentChanges) {
            sourceResult.documentChanges ??= [];
            let sourceEdit;
            if ('textDocument' in tsDocEdit) {
                const decoded = context.decodeEmbeddedDocumentUri(vscode_uri_1.URI.parse(tsDocEdit.textDocument.uri));
                const sourceScript = decoded && context.language.scripts.get(decoded[0]);
                const virtualCode = decoded && sourceScript?.generated?.embeddedCodes.get(decoded[1]);
                if (sourceScript && virtualCode) {
                    const embeddedDocument = context.documents.get(context.encodeEmbeddedDocumentUri(sourceScript.id, virtualCode.id), virtualCode.languageId, virtualCode.snapshot);
                    for (const [sourceScript, map] of context.language.maps.forEach(virtualCode)) {
                        const sourceDocument = context.documents.get(sourceScript.id, sourceScript.languageId, sourceScript.snapshot);
                        const docs = [sourceDocument, embeddedDocument, map];
                        sourceEdit = {
                            textDocument: {
                                uri: sourceDocument.uri,
                                version: versions[sourceDocument.uri] ?? null,
                            },
                            edits: [],
                        };
                        for (const tsEdit of tsDocEdit.edits) {
                            if (mode === 'rename' || mode === 'fileName' || mode === 'codeAction') {
                                let _data;
                                const range = (0, featureWorkers_1.getSourceRange)(docs, tsEdit.range, data => {
                                    _data = data;
                                    // fix https://github.com/johnsoncodehk/volar/issues/1091
                                    return (0, language_core_1.isRenameEnabled)(data);
                                });
                                if (range) {
                                    sourceEdit.edits.push({
                                        annotationId: 'annotationId' in tsEdit ? tsEdit.annotationId : undefined,
                                        newText: (0, language_core_1.resolveRenameEditText)(tsEdit.newText, _data),
                                        range,
                                    });
                                }
                            }
                            else {
                                const range = (0, featureWorkers_1.getSourceRange)(docs, tsEdit.range);
                                if (range) {
                                    sourceEdit.edits.push({
                                        annotationId: 'annotationId' in tsEdit ? tsEdit.annotationId : undefined,
                                        newText: tsEdit.newText,
                                        range,
                                    });
                                }
                            }
                        }
                        if (!sourceEdit.edits.length) {
                            sourceEdit = undefined;
                        }
                    }
                }
                else {
                    sourceEdit = tsDocEdit;
                }
            }
            else if (tsDocEdit.kind === 'create') {
                sourceEdit = tsDocEdit; // TODO: remove .ts?
            }
            else if (tsDocEdit.kind === 'rename') {
                const decoded = context.decodeEmbeddedDocumentUri(vscode_uri_1.URI.parse(tsDocEdit.oldUri));
                const sourceScript = decoded && context.language.scripts.get(decoded[0]);
                const virtualCode = decoded && sourceScript?.generated?.embeddedCodes.get(decoded[1]);
                if (virtualCode) {
                    for (const [sourceScript] of context.language.maps.forEach(virtualCode)) {
                        // TODO: check capability?
                        sourceEdit = {
                            kind: 'rename',
                            oldUri: sourceScript.id.toString(),
                            newUri: tsDocEdit.newUri, /* TODO: remove .ts? */
                            options: tsDocEdit.options,
                            annotationId: tsDocEdit.annotationId,
                        };
                    }
                }
                else {
                    sourceEdit = tsDocEdit;
                }
            }
            else if (tsDocEdit.kind === 'delete') {
                const decoded = context.decodeEmbeddedDocumentUri(vscode_uri_1.URI.parse(tsDocEdit.uri));
                const sourceScript = decoded && context.language.scripts.get(decoded[0]);
                const virtualCode = decoded && sourceScript?.generated?.embeddedCodes.get(decoded[1]);
                if (virtualCode) {
                    for (const [sourceScript] of context.language.maps.forEach(virtualCode)) {
                        // TODO: check capability?
                        sourceEdit = {
                            kind: 'delete',
                            uri: sourceScript.id.toString(),
                            options: tsDocEdit.options,
                            annotationId: tsDocEdit.annotationId,
                        };
                    }
                }
                else {
                    sourceEdit = tsDocEdit;
                }
            }
            if (sourceEdit) {
                pushEditToDocumentChanges(sourceResult.documentChanges, sourceEdit);
                hasResult = true;
            }
        }
    }
    if (hasResult) {
        return sourceResult;
    }
}
function pushEditToDocumentChanges(arr, item) {
    const current = arr.find(edit => 'textDocument' in edit
        && 'textDocument' in item
        && edit.textDocument.uri === item.textDocument.uri);
    if (current) {
        current.edits.push(...item.edits);
    }
    else {
        arr.push(item);
    }
}
//# sourceMappingURL=transform.js.map