"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createProxyLanguageService = createProxyLanguageService;
const language_core_1 = require("@volar/language-core");
const dedupe_1 = require("./dedupe");
const transform_1 = require("./transform");
const utils_1 = require("./utils");
const windowsPathReg = /\\/g;
function createProxyLanguageService(languageService) {
    const proxyCache = new Map();
    let getProxyMethod;
    return {
        initialize(language) {
            getProxyMethod = (target, p) => {
                switch (p) {
                    case 'getNavigationTree': return getNavigationTree(language, target[p]);
                    case 'getOutliningSpans': return getOutliningSpans(language, target[p]);
                    case 'getFormattingEditsForDocument': return getFormattingEditsForDocument(language, target[p]);
                    case 'getFormattingEditsForRange': return getFormattingEditsForRange(language, target[p]);
                    case 'getFormattingEditsAfterKeystroke': return getFormattingEditsAfterKeystroke(language, target[p]);
                    case 'getEditsForFileRename': return getEditsForFileRename(language, target[p]);
                    case 'getLinkedEditingRangeAtPosition': return getLinkedEditingRangeAtPosition(language, target[p]);
                    case 'prepareCallHierarchy': return prepareCallHierarchy(language, target[p]);
                    case 'provideCallHierarchyIncomingCalls': return provideCallHierarchyIncomingCalls(language, target[p]);
                    case 'provideCallHierarchyOutgoingCalls': return provideCallHierarchyOutgoingCalls(language, target[p]);
                    case 'organizeImports': return organizeImports(language, target[p]);
                    case 'getQuickInfoAtPosition': return getQuickInfoAtPosition(language, target[p]);
                    case 'getSignatureHelpItems': return getSignatureHelpItems(language, target[p]);
                    case 'getDocumentHighlights': return getDocumentHighlights(language, target[p]);
                    case 'getApplicableRefactors': return getApplicableRefactors(language, target[p]);
                    case 'getEditsForRefactor': return getEditsForRefactor(language, target[p]);
                    case 'getCombinedCodeFix': return getCombinedCodeFix(language, target[p]);
                    case 'getRenameInfo': return getRenameInfo(language, target[p]);
                    case 'getCodeFixesAtPosition': return getCodeFixesAtPosition(language, target[p]);
                    case 'getEncodedSemanticClassifications': return getEncodedSemanticClassifications(language, target[p]);
                    case 'getSyntacticDiagnostics': return getSyntacticDiagnostics(language, languageService, target[p]);
                    case 'getSemanticDiagnostics': return getSemanticDiagnostics(language, languageService, target[p]);
                    case 'getSuggestionDiagnostics': return getSuggestionDiagnostics(language, languageService, target[p]);
                    case 'getDefinitionAndBoundSpan': return getDefinitionAndBoundSpan(language, target[p]);
                    case 'findReferences': return findReferences(language, target[p]);
                    case 'getDefinitionAtPosition': return getDefinitionAtPosition(language, target[p]);
                    case 'getTypeDefinitionAtPosition': return getTypeDefinitionAtPosition(language, target[p]);
                    case 'getImplementationAtPosition': return getImplementationAtPosition(language, target[p]);
                    case 'findRenameLocations': return findRenameLocations(language, target[p]);
                    case 'getReferencesAtPosition': return getReferencesAtPosition(language, target[p]);
                    case 'getCompletionsAtPosition': return getCompletionsAtPosition(language, target[p]);
                    case 'getCompletionEntryDetails': return getCompletionEntryDetails(language, target[p]);
                    case 'provideInlayHints': return provideInlayHints(language, target[p]);
                    case 'getFileReferences': return getFileReferences(language, target[p]);
                    case 'getNavigateToItems': return getNavigateToItems(language, target[p]);
                }
            };
        },
        proxy: new Proxy(languageService, {
            get(target, p, receiver) {
                if (getProxyMethod) {
                    if (!proxyCache.has(p)) {
                        proxyCache.set(p, getProxyMethod(target, p));
                    }
                    const proxyMethod = proxyCache.get(p);
                    if (proxyMethod) {
                        return proxyMethod;
                    }
                }
                return Reflect.get(target, p, receiver);
            },
            set(target, p, value, receiver) {
                return Reflect.set(target, p, value, receiver);
            },
        }),
    };
}
// ignored methods
function getNavigationTree(language, getNavigationTree) {
    return filePath => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, targetScript] = (0, utils_1.getServiceScript)(language, fileName);
        if (serviceScript || targetScript?.associatedOnly) {
            const tree = getNavigationTree(targetScript.id);
            tree.childItems = undefined;
            return tree;
        }
        else {
            return getNavigationTree(fileName);
        }
    };
}
function getOutliningSpans(language, getOutliningSpans) {
    return filePath => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, targetScript] = (0, utils_1.getServiceScript)(language, fileName);
        if (serviceScript || targetScript?.associatedOnly) {
            return [];
        }
        else {
            return getOutliningSpans(fileName);
        }
    };
}
// proxy methods
function getFormattingEditsForDocument(language, getFormattingEditsForDocument) {
    return (filePath, options) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, targetScript, sourceScript] = (0, utils_1.getServiceScript)(language, fileName);
        if (targetScript?.associatedOnly) {
            return [];
        }
        if (serviceScript) {
            const map = language.maps.get(serviceScript.code, targetScript);
            if (!map.mappings.some(mapping => (0, language_core_1.isFormattingEnabled)(mapping.data))) {
                return [];
            }
            const edits = getFormattingEditsForDocument(targetScript.id, options);
            return edits
                .map(edit => (0, transform_1.transformTextChange)(sourceScript, language, serviceScript, edit, language_core_1.isFormattingEnabled)?.[1])
                .filter(edit => !!edit);
        }
        else {
            return getFormattingEditsForDocument(fileName, options);
        }
    };
}
function getFormattingEditsForRange(language, getFormattingEditsForRange) {
    return (filePath, start, end, options) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, targetScript, sourceScript] = (0, utils_1.getServiceScript)(language, fileName);
        if (targetScript?.associatedOnly) {
            return [];
        }
        if (serviceScript) {
            const generateStart = (0, transform_1.toGeneratedOffset)(language, serviceScript, sourceScript, start, language_core_1.isFormattingEnabled);
            const generateEnd = (0, transform_1.toGeneratedOffset)(language, serviceScript, sourceScript, end, language_core_1.isFormattingEnabled);
            if (generateStart !== undefined && generateEnd !== undefined) {
                const edits = getFormattingEditsForRange(targetScript.id, generateStart, generateEnd, options);
                return edits
                    .map(edit => (0, transform_1.transformTextChange)(sourceScript, language, serviceScript, edit, language_core_1.isFormattingEnabled)?.[1])
                    .filter(edit => !!edit);
            }
            return [];
        }
        else {
            return getFormattingEditsForRange(fileName, start, end, options);
        }
    };
}
function getFormattingEditsAfterKeystroke(language, getFormattingEditsAfterKeystroke) {
    return (filePath, position, key, options) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, targetScript, sourceScript] = (0, utils_1.getServiceScript)(language, fileName);
        if (targetScript?.associatedOnly) {
            return [];
        }
        if (serviceScript) {
            const generatePosition = (0, transform_1.toGeneratedOffset)(language, serviceScript, sourceScript, position, language_core_1.isFormattingEnabled);
            if (generatePosition !== undefined) {
                const edits = getFormattingEditsAfterKeystroke(targetScript.id, generatePosition, key, options);
                return edits
                    .map(edit => (0, transform_1.transformTextChange)(sourceScript, language, serviceScript, edit, language_core_1.isFormattingEnabled)?.[1])
                    .filter(edit => !!edit);
            }
            return [];
        }
        else {
            return getFormattingEditsAfterKeystroke(fileName, position, key, options);
        }
    };
}
function getEditsForFileRename(language, getEditsForFileRename) {
    return (oldFilePath, newFilePath, formatOptions, preferences) => {
        const edits = getEditsForFileRename(oldFilePath, newFilePath, formatOptions, preferences);
        return (0, transform_1.transformFileTextChanges)(language, edits, language_core_1.isRenameEnabled);
    };
}
function getLinkedEditingRangeAtPosition(language, getLinkedEditingRangeAtPosition) {
    return (filePath, position) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, targetScript, sourceScript] = (0, utils_1.getServiceScript)(language, fileName);
        if (targetScript?.associatedOnly) {
            return undefined;
        }
        if (serviceScript) {
            const generatePosition = (0, transform_1.toGeneratedOffset)(language, serviceScript, sourceScript, position, language_core_1.isLinkedEditingEnabled);
            if (generatePosition !== undefined) {
                const info = getLinkedEditingRangeAtPosition(targetScript.id, generatePosition);
                if (info) {
                    return {
                        ranges: info.ranges
                            .map(span => (0, transform_1.transformTextSpan)(sourceScript, language, serviceScript, span, language_core_1.isLinkedEditingEnabled)?.[1])
                            .filter(span => !!span),
                        wordPattern: info.wordPattern,
                    };
                }
            }
        }
        else {
            return getLinkedEditingRangeAtPosition(fileName, position);
        }
    };
}
function prepareCallHierarchy(language, prepareCallHierarchy) {
    return (filePath, position) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, targetScript, sourceScript] = (0, utils_1.getServiceScript)(language, fileName);
        if (targetScript?.associatedOnly) {
            return undefined;
        }
        if (serviceScript) {
            const generatePosition = (0, transform_1.toGeneratedOffset)(language, serviceScript, sourceScript, position, language_core_1.isCallHierarchyEnabled);
            if (generatePosition !== undefined) {
                const item = prepareCallHierarchy(targetScript.id, generatePosition);
                if (Array.isArray(item)) {
                    return item.map(item => (0, transform_1.transformCallHierarchyItem)(language, item, language_core_1.isCallHierarchyEnabled));
                }
                else if (item) {
                    return (0, transform_1.transformCallHierarchyItem)(language, item, language_core_1.isCallHierarchyEnabled);
                }
            }
        }
        else {
            return prepareCallHierarchy(fileName, position);
        }
    };
}
function provideCallHierarchyIncomingCalls(language, provideCallHierarchyIncomingCalls) {
    return (filePath, position) => {
        let calls = [];
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, targetScript, sourceScript] = (0, utils_1.getServiceScript)(language, fileName);
        if (targetScript?.associatedOnly) {
            return [];
        }
        if (serviceScript) {
            const generatePosition = (0, transform_1.toGeneratedOffset)(language, serviceScript, sourceScript, position, language_core_1.isCallHierarchyEnabled);
            if (generatePosition !== undefined) {
                calls = provideCallHierarchyIncomingCalls(targetScript.id, generatePosition);
            }
        }
        else {
            calls = provideCallHierarchyIncomingCalls(fileName, position);
        }
        return calls
            .map(call => {
            const from = (0, transform_1.transformCallHierarchyItem)(language, call.from, language_core_1.isCallHierarchyEnabled);
            const fromSpans = call.fromSpans
                .map(span => (0, transform_1.transformSpan)(language, call.from.file, span, language_core_1.isCallHierarchyEnabled)?.textSpan)
                .filter(span => !!span);
            return {
                from,
                fromSpans,
            };
        });
    };
}
function provideCallHierarchyOutgoingCalls(language, provideCallHierarchyOutgoingCalls) {
    return (filePath, position) => {
        let calls = [];
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, targetScript, sourceScript] = (0, utils_1.getServiceScript)(language, fileName);
        if (targetScript?.associatedOnly) {
            return [];
        }
        if (serviceScript) {
            const generatePosition = (0, transform_1.toGeneratedOffset)(language, serviceScript, sourceScript, position, language_core_1.isCallHierarchyEnabled);
            if (generatePosition !== undefined) {
                calls = provideCallHierarchyOutgoingCalls(targetScript.id, generatePosition);
            }
        }
        else {
            calls = provideCallHierarchyOutgoingCalls(fileName, position);
        }
        return calls
            .map(call => {
            const to = (0, transform_1.transformCallHierarchyItem)(language, call.to, language_core_1.isCallHierarchyEnabled);
            const fromSpans = call.fromSpans
                .map(span => serviceScript
                ? (0, transform_1.transformTextSpan)(sourceScript, language, serviceScript, span, language_core_1.isCallHierarchyEnabled)?.[1]
                : span)
                .filter(span => !!span);
            return {
                to,
                fromSpans,
            };
        });
    };
}
function organizeImports(language, organizeImports) {
    return (args, formatOptions, preferences) => {
        const unresolved = organizeImports(args, formatOptions, preferences);
        return (0, transform_1.transformFileTextChanges)(language, unresolved, language_core_1.isCodeActionsEnabled);
    };
}
function getQuickInfoAtPosition(language, getQuickInfoAtPosition) {
    return (filePath, position) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, targetScript, sourceScript] = (0, utils_1.getServiceScript)(language, fileName);
        if (targetScript?.associatedOnly) {
            return undefined;
        }
        if (serviceScript) {
            const infos = [];
            for (const [generatePosition] of (0, transform_1.toGeneratedOffsets)(language, serviceScript, sourceScript, position, language_core_1.isHoverEnabled)) {
                const info = getQuickInfoAtPosition(targetScript.id, generatePosition);
                if (info) {
                    const textSpan = (0, transform_1.transformTextSpan)(sourceScript, language, serviceScript, info.textSpan, language_core_1.isHoverEnabled)?.[1];
                    if (textSpan) {
                        infos.push({
                            ...info,
                            textSpan: textSpan,
                        });
                    }
                }
            }
            if (infos.length === 1) {
                return infos[0];
            }
            else if (infos.length >= 2) {
                const combine = { ...infos[0] };
                combine.displayParts = combine.displayParts?.slice();
                combine.documentation = combine.documentation?.slice();
                combine.tags = combine.tags?.slice();
                const displayPartsStrs = new Set([displayPartsToString(infos[0].displayParts)]);
                const documentationStrs = new Set([displayPartsToString(infos[0].documentation)]);
                const tagsStrs = new Set();
                for (const tag of infos[0].tags ?? []) {
                    tagsStrs.add(tag.name + '__volar__' + displayPartsToString(tag.text));
                }
                for (let i = 1; i < infos.length; i++) {
                    const { displayParts, documentation, tags } = infos[i];
                    if (displayParts?.length && !displayPartsStrs.has(displayPartsToString(displayParts))) {
                        displayPartsStrs.add(displayPartsToString(displayParts));
                        combine.displayParts ??= [];
                        combine.displayParts.push({ ...displayParts[0], text: '\n\n' + displayParts[0].text });
                        combine.displayParts.push(...displayParts.slice(1));
                    }
                    if (documentation?.length && !documentationStrs.has(displayPartsToString(documentation))) {
                        documentationStrs.add(displayPartsToString(documentation));
                        combine.documentation ??= [];
                        combine.documentation.push({ ...documentation[0], text: '\n\n' + documentation[0].text });
                        combine.documentation.push(...documentation.slice(1));
                    }
                    for (const tag of tags ?? []) {
                        if (!tagsStrs.has(tag.name + '__volar__' + displayPartsToString(tag.text))) {
                            tagsStrs.add(tag.name + '__volar__' + displayPartsToString(tag.text));
                            combine.tags ??= [];
                            combine.tags.push(tag);
                        }
                    }
                }
                return combine;
            }
        }
        else {
            return getQuickInfoAtPosition(fileName, position);
        }
    };
}
function getSignatureHelpItems(language, getSignatureHelpItems) {
    return (filePath, position, options) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, targetScript, sourceScript] = (0, utils_1.getServiceScript)(language, fileName);
        if (targetScript?.associatedOnly) {
            return undefined;
        }
        if (serviceScript) {
            const generatePosition = (0, transform_1.toGeneratedOffset)(language, serviceScript, sourceScript, position, language_core_1.isSignatureHelpEnabled);
            if (generatePosition !== undefined) {
                const result = getSignatureHelpItems(targetScript.id, generatePosition, options);
                if (result) {
                    const applicableSpan = (0, transform_1.transformTextSpan)(sourceScript, language, serviceScript, result.applicableSpan, language_core_1.isSignatureHelpEnabled)?.[1];
                    if (applicableSpan) {
                        return {
                            ...result,
                            applicableSpan,
                        };
                    }
                }
            }
        }
        else {
            return getSignatureHelpItems(fileName, position, options);
        }
    };
}
function getDocumentHighlights(language, getDocumentHighlights) {
    return (filePath, position, filesToSearch) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const unresolved = linkedCodeFeatureWorker(language, fileName, position, language_core_1.isHighlightEnabled, (fileName, position) => getDocumentHighlights(fileName, position, filesToSearch), function* (result) {
            for (const ref of result) {
                for (const reference of ref.highlightSpans) {
                    yield [reference.fileName ?? ref.fileName, reference.textSpan.start];
                }
            }
        });
        const resolved = unresolved
            .flat()
            .map(highlights => {
            return {
                ...highlights,
                highlightSpans: highlights.highlightSpans
                    .map(span => {
                    const { textSpan } = (0, transform_1.transformSpan)(language, span.fileName ?? highlights.fileName, span.textSpan, language_core_1.isHighlightEnabled) ?? {};
                    if (textSpan) {
                        return {
                            ...span,
                            contextSpan: (0, transform_1.transformSpan)(language, span.fileName ?? highlights.fileName, span.contextSpan, language_core_1.isHighlightEnabled)?.textSpan,
                            textSpan,
                        };
                    }
                })
                    .filter(span => !!span),
            };
        });
        return resolved;
    };
}
function getApplicableRefactors(language, getApplicableRefactors) {
    return (filePath, positionOrRange, preferences, triggerReason, kind, includeInteractiveActions) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, targetScript, sourceScript] = (0, utils_1.getServiceScript)(language, fileName);
        if (targetScript?.associatedOnly) {
            return [];
        }
        if (serviceScript) {
            if (typeof positionOrRange === 'number') {
                const generatePosition = (0, transform_1.toGeneratedOffset)(language, serviceScript, sourceScript, positionOrRange, language_core_1.isCodeActionsEnabled);
                if (generatePosition !== undefined) {
                    return getApplicableRefactors(targetScript.id, generatePosition, preferences, triggerReason, kind, includeInteractiveActions);
                }
            }
            else {
                for (const [generatedStart, generatedEnd] of (0, transform_1.toGeneratedRanges)(language, serviceScript, sourceScript, positionOrRange.pos, positionOrRange.end, language_core_1.isCodeActionsEnabled)) {
                    return getApplicableRefactors(targetScript.id, { pos: generatedStart, end: generatedEnd }, preferences, triggerReason, kind, includeInteractiveActions);
                }
            }
            return [];
        }
        else {
            return getApplicableRefactors(fileName, positionOrRange, preferences, triggerReason, kind, includeInteractiveActions);
        }
    };
}
function getEditsForRefactor(language, getEditsForRefactor) {
    return (filePath, formatOptions, positionOrRange, refactorName, actionName, preferences) => {
        let edits;
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, targetScript, sourceScript] = (0, utils_1.getServiceScript)(language, fileName);
        if (targetScript?.associatedOnly) {
            return undefined;
        }
        if (serviceScript) {
            if (typeof positionOrRange === 'number') {
                const generatePosition = (0, transform_1.toGeneratedOffset)(language, serviceScript, sourceScript, positionOrRange, language_core_1.isCodeActionsEnabled);
                if (generatePosition !== undefined) {
                    edits = getEditsForRefactor(targetScript.id, formatOptions, generatePosition, refactorName, actionName, preferences);
                }
            }
            else {
                for (const [generatedStart, generatedEnd] of (0, transform_1.toGeneratedRanges)(language, serviceScript, sourceScript, positionOrRange.pos, positionOrRange.end, language_core_1.isCodeActionsEnabled)) {
                    edits = getEditsForRefactor(targetScript.id, formatOptions, { pos: generatedStart, end: generatedEnd }, refactorName, actionName, preferences);
                }
            }
        }
        else {
            edits = getEditsForRefactor(fileName, formatOptions, positionOrRange, refactorName, actionName, preferences);
        }
        if (edits) {
            edits.edits = (0, transform_1.transformFileTextChanges)(language, edits.edits, language_core_1.isCodeActionsEnabled);
            return edits;
        }
    };
}
function getCombinedCodeFix(language, getCombinedCodeFix) {
    return (...args) => {
        const codeActions = getCombinedCodeFix(...args);
        codeActions.changes = (0, transform_1.transformFileTextChanges)(language, codeActions.changes, language_core_1.isCodeActionsEnabled);
        return codeActions;
    };
}
function getRenameInfo(language, getRenameInfo) {
    return (filePath, position, options) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, targetScript, sourceScript] = (0, utils_1.getServiceScript)(language, fileName);
        if (targetScript?.associatedOnly) {
            return {
                canRename: false,
                localizedErrorMessage: "Cannot rename"
            };
        }
        if (serviceScript) {
            let failed;
            for (const [generateOffset] of (0, transform_1.toGeneratedOffsets)(language, serviceScript, sourceScript, position, language_core_1.isRenameEnabled)) {
                const info = getRenameInfo(targetScript.id, generateOffset, options);
                if (info.canRename) {
                    const span = (0, transform_1.transformTextSpan)(sourceScript, language, serviceScript, info.triggerSpan, language_core_1.isRenameEnabled)?.[1];
                    if (span) {
                        info.triggerSpan = span;
                        return info;
                    }
                }
                else {
                    failed = info;
                }
            }
            if (failed) {
                return failed;
            }
            return {
                canRename: false,
                localizedErrorMessage: 'Failed to get rename locations',
            };
        }
        else {
            return getRenameInfo(fileName, position, options);
        }
    };
}
function getCodeFixesAtPosition(language, getCodeFixesAtPosition) {
    return (filePath, start, end, errorCodes, formatOptions, preferences) => {
        let fixes = [];
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, targetScript, sourceScript] = (0, utils_1.getServiceScript)(language, fileName);
        if (targetScript?.associatedOnly) {
            return [];
        }
        if (serviceScript) {
            const generateStart = (0, transform_1.toGeneratedOffset)(language, serviceScript, sourceScript, start, language_core_1.isCodeActionsEnabled);
            const generateEnd = (0, transform_1.toGeneratedOffset)(language, serviceScript, sourceScript, end, language_core_1.isCodeActionsEnabled);
            if (generateStart !== undefined && generateEnd !== undefined) {
                fixes = getCodeFixesAtPosition(targetScript.id, generateStart, generateEnd, errorCodes, formatOptions, preferences);
            }
        }
        else {
            fixes = getCodeFixesAtPosition(fileName, start, end, errorCodes, formatOptions, preferences);
        }
        fixes = fixes.map(fix => {
            fix.changes = (0, transform_1.transformFileTextChanges)(language, fix.changes, language_core_1.isCodeActionsEnabled);
            return fix;
        });
        return fixes;
    };
}
function getEncodedSemanticClassifications(language, getEncodedSemanticClassifications) {
    return (filePath, span, format) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, targetScript, sourceScript] = (0, utils_1.getServiceScript)(language, fileName);
        if (targetScript?.associatedOnly) {
            return {
                spans: [],
                endOfLineState: 0
            };
        }
        if (serviceScript) {
            let start;
            let end;
            const map = language.maps.get(serviceScript.code, targetScript);
            for (const mapping of map.mappings) {
                // TODO reuse the logic from language service
                if ((0, language_core_1.isSemanticTokensEnabled)(mapping.data) && mapping.sourceOffsets[0] >= span.start && mapping.sourceOffsets[0] <= span.start + span.length) {
                    start ??= mapping.generatedOffsets[0];
                    end ??= mapping.generatedOffsets[mapping.generatedOffsets.length - 1] + (mapping.generatedLengths ?? mapping.lengths)[mapping.lengths.length - 1];
                    start = Math.min(start, mapping.generatedOffsets[0]);
                    end = Math.max(end, mapping.generatedOffsets[mapping.generatedOffsets.length - 1] + (mapping.generatedLengths ?? mapping.lengths)[mapping.lengths.length - 1]);
                }
            }
            start ??= 0;
            end ??= targetScript.snapshot.getLength();
            const mappingOffset = (0, transform_1.getMappingOffset)(language, serviceScript);
            start += mappingOffset;
            end += mappingOffset;
            const result = getEncodedSemanticClassifications(targetScript.id, { start, length: end - start }, format);
            const spans = [];
            for (let i = 0; i < result.spans.length; i += 3) {
                for (const [_, sourceStart, sourceEnd] of (0, transform_1.toSourceRanges)(sourceScript, language, serviceScript, result.spans[i], result.spans[i] + result.spans[i + 1], language_core_1.isSemanticTokensEnabled)) {
                    spans.push(sourceStart, sourceEnd - sourceStart, result.spans[i + 2]);
                    break;
                }
            }
            result.spans = spans;
            return result;
        }
        else {
            return getEncodedSemanticClassifications(fileName, span, format);
        }
    };
}
function getSyntacticDiagnostics(language, languageService, getSyntacticDiagnostics) {
    return filePath => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, targetScript, sourceScript] = (0, utils_1.getServiceScript)(language, fileName);
        if (targetScript?.associatedOnly) {
            return [];
        }
        return getSyntacticDiagnostics(targetScript?.id ?? fileName)
            .map(d => (0, transform_1.transformDiagnostic)(language, d, languageService.getProgram(), false))
            .filter(d => !!d)
            .filter(d => !serviceScript || language.scripts.get(d.file.fileName) === sourceScript);
    };
}
function getSemanticDiagnostics(language, languageService, getSemanticDiagnostics) {
    return filePath => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, targetScript, sourceScript] = (0, utils_1.getServiceScript)(language, fileName);
        if (targetScript?.associatedOnly) {
            return [];
        }
        return getSemanticDiagnostics(targetScript?.id ?? fileName)
            .map(d => (0, transform_1.transformDiagnostic)(language, d, languageService.getProgram(), false))
            .filter(d => !!d)
            .filter(d => !serviceScript || !d.file || language.scripts.get(d.file.fileName) === sourceScript);
    };
}
function getSuggestionDiagnostics(language, languageService, getSuggestionDiagnostics) {
    return filePath => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, targetScript, sourceScript] = (0, utils_1.getServiceScript)(language, fileName);
        if (targetScript?.associatedOnly) {
            return [];
        }
        return getSuggestionDiagnostics(targetScript?.id ?? fileName)
            .map(d => (0, transform_1.transformDiagnostic)(language, d, languageService.getProgram(), false))
            .filter(d => !!d)
            .filter(d => !serviceScript || !d.file || language.scripts.get(d.file.fileName) === sourceScript);
    };
}
function getDefinitionAndBoundSpan(language, getDefinitionAndBoundSpan) {
    return (filePath, position) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const unresolved = linkedCodeFeatureWorker(language, fileName, position, language_core_1.isDefinitionEnabled, (fileName, position) => getDefinitionAndBoundSpan(fileName, position), function* (result) {
            for (const ref of result.definitions ?? []) {
                yield [ref.fileName, ref.textSpan.start];
            }
        });
        const textSpan = unresolved
            .map(s => (0, transform_1.transformSpan)(language, fileName, s.textSpan, language_core_1.isDefinitionEnabled)?.textSpan)
            .filter(s => !!s)[0];
        if (!textSpan) {
            return;
        }
        const definitions = unresolved
            .map(s => s.definitions
            ?.map(s => (0, transform_1.transformDocumentSpan)(language, s, language_core_1.isDefinitionEnabled, s.fileName !== fileName))
            .filter(s => !!s)
            ?? [])
            .flat();
        return {
            textSpan,
            definitions: (0, dedupe_1.dedupeDocumentSpans)(definitions),
        };
    };
}
function findReferences(language, findReferences) {
    return (filePath, position) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const unresolved = linkedCodeFeatureWorker(language, fileName, position, language_core_1.isReferencesEnabled, (fileName, position) => findReferences(fileName, position), function* (result) {
            for (const ref of result) {
                for (const reference of ref.references) {
                    yield [reference.fileName, reference.textSpan.start];
                }
            }
        });
        const resolved = unresolved
            .flat()
            .map(symbol => {
            const definition = (0, transform_1.transformDocumentSpan)(language, symbol.definition, language_core_1.isDefinitionEnabled, true);
            return {
                definition,
                references: symbol.references
                    .map(r => (0, transform_1.transformDocumentSpan)(language, r, language_core_1.isReferencesEnabled))
                    .filter(r => !!r),
            };
        });
        return resolved;
    };
}
function getDefinitionAtPosition(language, getDefinitionAtPosition) {
    return (filePath, position) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const unresolved = linkedCodeFeatureWorker(language, fileName, position, language_core_1.isDefinitionEnabled, (fileName, position) => getDefinitionAtPosition(fileName, position), function* (result) {
            for (const ref of result) {
                yield [ref.fileName, ref.textSpan.start];
            }
        });
        const resolved = unresolved
            .flat()
            .map(s => (0, transform_1.transformDocumentSpan)(language, s, language_core_1.isDefinitionEnabled, s.fileName !== fileName))
            .filter(s => !!s);
        return (0, dedupe_1.dedupeDocumentSpans)(resolved);
    };
}
function getTypeDefinitionAtPosition(language, getTypeDefinitionAtPosition) {
    return (filePath, position) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const unresolved = linkedCodeFeatureWorker(language, fileName, position, language_core_1.isTypeDefinitionEnabled, (fileName, position) => getTypeDefinitionAtPosition(fileName, position), function* (result) {
            for (const ref of result) {
                yield [ref.fileName, ref.textSpan.start];
            }
        });
        const resolved = unresolved
            .flat()
            .map(s => (0, transform_1.transformDocumentSpan)(language, s, language_core_1.isTypeDefinitionEnabled))
            .filter(s => !!s);
        return (0, dedupe_1.dedupeDocumentSpans)(resolved);
    };
}
function getImplementationAtPosition(language, getImplementationAtPosition) {
    return (filePath, position) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const unresolved = linkedCodeFeatureWorker(language, fileName, position, language_core_1.isImplementationEnabled, (fileName, position) => getImplementationAtPosition(fileName, position), function* (result) {
            for (const ref of result) {
                yield [ref.fileName, ref.textSpan.start];
            }
        });
        const resolved = unresolved
            .flat()
            .map(s => (0, transform_1.transformDocumentSpan)(language, s, language_core_1.isImplementationEnabled))
            .filter(s => !!s);
        return (0, dedupe_1.dedupeDocumentSpans)(resolved);
    };
}
function findRenameLocations(language, findRenameLocations) {
    return (filePath, position, findInStrings, findInComments, preferences) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const unresolved = linkedCodeFeatureWorker(language, fileName, position, language_core_1.isRenameEnabled, (fileName, position) => findRenameLocations(fileName, position, findInStrings, findInComments, preferences), function* (result) {
            for (const ref of result) {
                yield [ref.fileName, ref.textSpan.start];
            }
        });
        const resolved = unresolved
            .flat()
            .map(s => (0, transform_1.transformDocumentSpan)(language, s, language_core_1.isRenameEnabled))
            .filter(s => !!s);
        return (0, dedupe_1.dedupeDocumentSpans)(resolved);
    };
}
function getReferencesAtPosition(language, getReferencesAtPosition) {
    return (filePath, position) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const unresolved = linkedCodeFeatureWorker(language, fileName, position, language_core_1.isReferencesEnabled, (fileName, position) => getReferencesAtPosition(fileName, position), function* (result) {
            for (const ref of result) {
                yield [ref.fileName, ref.textSpan.start];
            }
        });
        const resolved = unresolved
            .flat()
            .map(s => (0, transform_1.transformDocumentSpan)(language, s, language_core_1.isReferencesEnabled))
            .filter(s => !!s);
        return (0, dedupe_1.dedupeDocumentSpans)(resolved);
    };
}
function getCompletionsAtPosition(language, getCompletionsAtPosition) {
    return (filePath, position, options, formattingSettings) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, targetScript, sourceScript] = (0, utils_1.getServiceScript)(language, fileName);
        if (targetScript?.associatedOnly) {
            return undefined;
        }
        if (serviceScript) {
            const results = [];
            for (const [generatedOffset, mapping] of (0, transform_1.toGeneratedOffsets)(language, serviceScript, sourceScript, position, language_core_1.isCompletionEnabled)) {
                const result = getCompletionsAtPosition(targetScript.id, generatedOffset, options, formattingSettings);
                if (!result) {
                    continue;
                }
                if (typeof mapping.data.completion === 'object' && mapping.data.completion.onlyImport) {
                    result.entries = result.entries.filter(entry => !!entry.sourceDisplay);
                }
                for (const entry of result.entries) {
                    entry.replacementSpan = entry.replacementSpan && (0, transform_1.transformTextSpan)(sourceScript, language, serviceScript, entry.replacementSpan, language_core_1.isCompletionEnabled)?.[1];
                }
                result.optionalReplacementSpan = result.optionalReplacementSpan
                    && (0, transform_1.transformTextSpan)(sourceScript, language, serviceScript, result.optionalReplacementSpan, language_core_1.isCompletionEnabled)?.[1];
                const isAdditional = typeof mapping.data.completion === 'object' && mapping.data.completion.isAdditional;
                if (isAdditional) {
                    results.push(result);
                }
                else {
                    results.unshift(result);
                }
            }
            if (results.length) {
                return {
                    ...results[0],
                    entries: results
                        .map(additionalResult => additionalResult.entries)
                        .flat(),
                };
            }
        }
        else {
            return getCompletionsAtPosition(fileName, position, options, formattingSettings);
        }
    };
}
function getCompletionEntryDetails(language, getCompletionEntryDetails) {
    return (filePath, position, entryName, formatOptions, source, preferences, data) => {
        let details;
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, targetScript, sourceScript] = (0, utils_1.getServiceScript)(language, fileName);
        if (targetScript?.associatedOnly) {
            return undefined;
        }
        if (serviceScript) {
            const generatePosition = (0, transform_1.toGeneratedOffset)(language, serviceScript, sourceScript, position, language_core_1.isCompletionEnabled);
            if (generatePosition !== undefined) {
                details = getCompletionEntryDetails(targetScript.id, generatePosition, entryName, formatOptions, source, preferences, data);
            }
        }
        else {
            return getCompletionEntryDetails(fileName, position, entryName, formatOptions, source, preferences, data);
        }
        if (details?.codeActions) {
            for (const codeAction of details.codeActions) {
                codeAction.changes = (0, transform_1.transformFileTextChanges)(language, codeAction.changes, language_core_1.isCompletionEnabled);
            }
        }
        return details;
    };
}
function provideInlayHints(language, provideInlayHints) {
    return (filePath, span, preferences) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, targetScript, sourceScript] = (0, utils_1.getServiceScript)(language, fileName);
        if (targetScript?.associatedOnly) {
            return [];
        }
        if (serviceScript) {
            let start;
            let end;
            const map = language.maps.get(serviceScript.code, targetScript);
            for (const mapping of map.mappings) {
                if ((0, language_core_1.isInlayHintsEnabled)(mapping.data) && mapping.sourceOffsets[0] >= span.start && mapping.sourceOffsets[0] <= span.start + span.length) {
                    start ??= mapping.generatedOffsets[0];
                    end ??= mapping.generatedOffsets[mapping.generatedOffsets.length - 1];
                    start = Math.min(start, mapping.generatedOffsets[0]);
                    end = Math.max(end, mapping.generatedOffsets[mapping.generatedOffsets.length - 1]);
                }
            }
            if (start === undefined || end === undefined) {
                start = 0;
                end = 0;
            }
            const mappingOffset = (0, transform_1.getMappingOffset)(language, serviceScript);
            start += mappingOffset;
            end += mappingOffset;
            const result = provideInlayHints(targetScript.id, { start, length: end - start }, preferences);
            const hints = [];
            for (const hint of result) {
                const sourcePosition = (0, transform_1.toSourceOffset)(sourceScript, language, serviceScript, hint.position, language_core_1.isInlayHintsEnabled);
                if (sourcePosition !== undefined) {
                    hints.push({
                        ...hint,
                        position: sourcePosition[1],
                    });
                }
            }
            return hints;
        }
        else {
            return provideInlayHints(fileName, span, preferences);
        }
    };
}
function getFileReferences(language, getFileReferences) {
    return filePath => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const unresolved = getFileReferences(fileName);
        const resolved = unresolved
            .map(s => (0, transform_1.transformDocumentSpan)(language, s, language_core_1.isReferencesEnabled))
            .filter(s => !!s);
        return (0, dedupe_1.dedupeDocumentSpans)(resolved);
    };
}
function getNavigateToItems(language, getNavigateToItems) {
    return (...args) => {
        const unresolved = getNavigateToItems(...args);
        const resolved = unresolved
            .map(s => (0, transform_1.transformDocumentSpan)(language, s, language_core_1.isReferencesEnabled))
            .filter(s => !!s);
        return (0, dedupe_1.dedupeDocumentSpans)(resolved);
    };
}
function linkedCodeFeatureWorker(language, fileName, position, filter, worker, getLinkedCodes) {
    const results = [];
    const processedFilePositions = new Set();
    const [serviceScript, targetScript, sourceScript] = (0, utils_1.getServiceScript)(language, fileName);
    if (serviceScript) {
        for (const [generatedOffset] of (0, transform_1.toGeneratedOffsets)(language, serviceScript, sourceScript, position, filter)) {
            process(targetScript.id, generatedOffset);
        }
    }
    else {
        process(fileName, position);
    }
    return results;
    function process(fileName, position) {
        if (processedFilePositions.has(fileName + ':' + position)) {
            return;
        }
        processedFilePositions.add(fileName + ':' + position);
        const result = worker(fileName, position);
        if (!result) {
            return;
        }
        results.push(result);
        for (const ref of getLinkedCodes(result)) {
            processedFilePositions.add(ref[0] + ':' + ref[1]);
            const [serviceScript] = (0, utils_1.getServiceScript)(language, ref[0]);
            if (!serviceScript) {
                continue;
            }
            const linkedCodeMap = language.linkedCodeMaps.get(serviceScript.code);
            if (!linkedCodeMap) {
                continue;
            }
            const mappingOffset = (0, transform_1.getMappingOffset)(language, serviceScript);
            for (const linkedCodeOffset of linkedCodeMap.getLinkedOffsets(ref[1] - mappingOffset)) {
                process(ref[0], linkedCodeOffset + mappingOffset);
            }
        }
    }
}
function displayPartsToString(displayParts) {
    if (displayParts) {
        return displayParts.map(displayPart => displayPart.text).join('');
    }
    return '';
}
//# sourceMappingURL=proxyLanguageService.js.map