"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.decorateLanguageService = void 0;
const language_core_1 = require("@volar/language-core");
const dedupe_1 = require("./dedupe");
const utils_1 = require("./utils");
const transform_1 = require("./transform");
const windowsPathReg = /\\/g;
function decorateLanguageService(language, languageService) {
    // ignored methods
    const { getNavigationTree, getOutliningSpans, } = languageService;
    languageService.getNavigationTree = filePath => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript] = (0, utils_1.getServiceScript)(language, fileName);
        if (serviceScript) {
            const tree = getNavigationTree(fileName);
            tree.childItems = undefined;
            return tree;
        }
        else {
            return getNavigationTree(fileName);
        }
    };
    languageService.getOutliningSpans = filePath => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript] = (0, utils_1.getServiceScript)(language, fileName);
        if (serviceScript) {
            return [];
        }
        else {
            return getOutliningSpans(fileName);
        }
    };
    // methods
    const { findReferences, findRenameLocations, getCompletionEntryDetails, getCompletionsAtPosition, getDefinitionAndBoundSpan, getDefinitionAtPosition, getFileReferences, getFormattingEditsForDocument, getFormattingEditsForRange, getFormattingEditsAfterKeystroke, getImplementationAtPosition, getLinkedEditingRangeAtPosition, getQuickInfoAtPosition, getSignatureHelpItems, getReferencesAtPosition, getSemanticDiagnostics, getSyntacticDiagnostics, getSuggestionDiagnostics, getTypeDefinitionAtPosition, getEncodedSemanticClassifications, getDocumentHighlights, getApplicableRefactors, getEditsForFileRename, getEditsForRefactor, getRenameInfo, getCodeFixesAtPosition, prepareCallHierarchy, provideCallHierarchyIncomingCalls, provideCallHierarchyOutgoingCalls, provideInlayHints, organizeImports, } = languageService;
    languageService.getFormattingEditsForDocument = (filePath, options) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, sourceScript, map] = (0, utils_1.getServiceScript)(language, fileName);
        if (serviceScript) {
            if (!map.mappings.some(mapping => (0, language_core_1.isFormattingEnabled)(mapping.data))) {
                return [];
            }
            const edits = getFormattingEditsForDocument(fileName, options);
            return edits
                .map(edit => (0, transform_1.transformTextChange)(sourceScript, map, edit, language_core_1.isFormattingEnabled))
                .filter(utils_1.notEmpty);
        }
        else {
            return getFormattingEditsForDocument(fileName, options);
        }
    };
    languageService.getFormattingEditsForRange = (filePath, start, end, options) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, sourceScript, map] = (0, utils_1.getServiceScript)(language, fileName);
        if (serviceScript) {
            const generateStart = (0, transform_1.toGeneratedOffset)(sourceScript, map, start, language_core_1.isFormattingEnabled);
            const generateEnd = (0, transform_1.toGeneratedOffset)(sourceScript, map, end, language_core_1.isFormattingEnabled);
            if (generateStart !== undefined && generateEnd !== undefined) {
                const edits = getFormattingEditsForRange(fileName, generateStart, generateEnd, options);
                return edits
                    .map(edit => (0, transform_1.transformTextChange)(sourceScript, map, edit, language_core_1.isFormattingEnabled))
                    .filter(utils_1.notEmpty);
            }
            return [];
        }
        else {
            return getFormattingEditsForRange(fileName, start, end, options);
        }
    };
    languageService.getFormattingEditsAfterKeystroke = (filePath, position, key, options) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, sourceScript, map] = (0, utils_1.getServiceScript)(language, fileName);
        if (serviceScript) {
            const generatePosition = (0, transform_1.toGeneratedOffset)(sourceScript, map, position, language_core_1.isFormattingEnabled);
            if (generatePosition !== undefined) {
                const edits = getFormattingEditsAfterKeystroke(fileName, generatePosition, key, options);
                return edits
                    .map(edit => (0, transform_1.transformTextChange)(sourceScript, map, edit, language_core_1.isFormattingEnabled))
                    .filter(utils_1.notEmpty);
            }
            return [];
        }
        else {
            return getFormattingEditsAfterKeystroke(fileName, position, key, options);
        }
    };
    languageService.getEditsForFileRename = (oldFilePath, newFilePath, formatOptions, preferences) => {
        const edits = getEditsForFileRename(oldFilePath, newFilePath, formatOptions, preferences);
        return edits
            .map(edit => (0, transform_1.transformFileTextChanges)(language, edit, language_core_1.isRenameEnabled))
            .filter(utils_1.notEmpty);
    };
    languageService.getLinkedEditingRangeAtPosition = (filePath, position) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, sourceScript, map] = (0, utils_1.getServiceScript)(language, fileName);
        if (serviceScript) {
            const generatePosition = (0, transform_1.toGeneratedOffset)(sourceScript, map, position, language_core_1.isLinkedEditingEnabled);
            if (generatePosition !== undefined) {
                const info = getLinkedEditingRangeAtPosition(fileName, generatePosition);
                if (info) {
                    return {
                        ranges: info.ranges
                            .map(span => (0, transform_1.transformTextSpan)(sourceScript, map, span, language_core_1.isLinkedEditingEnabled))
                            .filter(utils_1.notEmpty),
                        wordPattern: info.wordPattern,
                    };
                }
            }
        }
        else {
            return getLinkedEditingRangeAtPosition(fileName, position);
        }
    };
    languageService.prepareCallHierarchy = (filePath, position) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, sourceScript, map] = (0, utils_1.getServiceScript)(language, fileName);
        if (serviceScript) {
            const generatePosition = (0, transform_1.toGeneratedOffset)(sourceScript, map, position, language_core_1.isCallHierarchyEnabled);
            if (generatePosition !== undefined) {
                const item = prepareCallHierarchy(fileName, 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);
        }
    };
    languageService.provideCallHierarchyIncomingCalls = (filePath, position) => {
        let calls = [];
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, sourceScript, map] = (0, utils_1.getServiceScript)(language, fileName);
        if (serviceScript) {
            const generatePosition = (0, transform_1.toGeneratedOffset)(sourceScript, map, position, language_core_1.isCallHierarchyEnabled);
            if (generatePosition !== undefined) {
                calls = provideCallHierarchyIncomingCalls(fileName, 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(utils_1.notEmpty);
            return {
                from,
                fromSpans,
            };
        });
    };
    languageService.provideCallHierarchyOutgoingCalls = (filePath, position) => {
        let calls = [];
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, sourceScript, map] = (0, utils_1.getServiceScript)(language, fileName);
        if (serviceScript) {
            const generatePosition = (0, transform_1.toGeneratedOffset)(sourceScript, map, position, language_core_1.isCallHierarchyEnabled);
            if (generatePosition !== undefined) {
                calls = provideCallHierarchyOutgoingCalls(fileName, 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 => sourceScript
                ? (0, transform_1.transformTextSpan)(sourceScript, map, span, language_core_1.isCallHierarchyEnabled)
                : span)
                .filter(utils_1.notEmpty);
            return {
                to,
                fromSpans,
            };
        });
    };
    languageService.organizeImports = (args, formatOptions, preferences) => {
        const unresolved = organizeImports(args, formatOptions, preferences);
        const resolved = unresolved
            .map(changes => (0, transform_1.transformFileTextChanges)(language, changes, language_core_1.isCodeActionsEnabled))
            .filter(utils_1.notEmpty);
        return resolved;
    };
    languageService.getQuickInfoAtPosition = (filePath, position) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, sourceScript, map] = (0, utils_1.getServiceScript)(language, fileName);
        if (serviceScript) {
            const infos = [];
            for (const [generatePosition, mapping] of (0, transform_1.toGeneratedOffsets)(sourceScript, map, position)) {
                if (!(0, language_core_1.isHoverEnabled)(mapping.data)) {
                    continue;
                }
                const info = getQuickInfoAtPosition(fileName, generatePosition);
                if (info) {
                    const textSpan = (0, transform_1.transformTextSpan)(sourceScript, map, info.textSpan, language_core_1.isHoverEnabled);
                    if (textSpan) {
                        infos.push({
                            ...info,
                            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);
        }
    };
    languageService.getSignatureHelpItems = (filePath, position, options) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, sourceScript, map] = (0, utils_1.getServiceScript)(language, fileName);
        if (serviceScript) {
            const generatePosition = (0, transform_1.toGeneratedOffset)(sourceScript, map, position, language_core_1.isSignatureHelpEnabled);
            if (generatePosition !== undefined) {
                const result = getSignatureHelpItems(fileName, generatePosition, options);
                if (result) {
                    const applicableSpan = (0, transform_1.transformTextSpan)(sourceScript, map, result.applicableSpan, language_core_1.isSignatureHelpEnabled);
                    if (applicableSpan) {
                        return {
                            ...result,
                            applicableSpan,
                        };
                    }
                }
            }
        }
        else {
            return getSignatureHelpItems(fileName, position, options);
        }
    };
    languageService.getDocumentHighlights = (filePath, position, filesToSearch) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const unresolved = linkedCodeFeatureWorker(fileName, position, language_core_1.isHighlightEnabled, 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)?.textSpan;
                    if (textSpan) {
                        return {
                            ...span,
                            contextSpan: (0, transform_1.transformSpan)(language, span.fileName ?? highlights.fileName, span.contextSpan, language_core_1.isHighlightEnabled)?.textSpan,
                            textSpan,
                        };
                    }
                })
                    .filter(utils_1.notEmpty),
            };
        });
        return resolved;
    };
    languageService.getApplicableRefactors = (filePath, positionOrRange, preferences, triggerReason, kind, includeInteractiveActions) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, sourceScript, map] = (0, utils_1.getServiceScript)(language, fileName);
        if (serviceScript) {
            const generatePosition = (0, transform_1.toGeneratedOffset)(sourceScript, map, typeof positionOrRange === 'number' ? positionOrRange : positionOrRange.pos, language_core_1.isCodeActionsEnabled);
            if (generatePosition !== undefined) {
                const por = typeof positionOrRange === 'number'
                    ? generatePosition
                    : {
                        pos: generatePosition,
                        end: generatePosition + positionOrRange.end - positionOrRange.pos,
                    };
                return getApplicableRefactors(fileName, por, preferences, triggerReason, kind, includeInteractiveActions);
            }
            return [];
        }
        else {
            return getApplicableRefactors(fileName, positionOrRange, preferences, triggerReason, kind, includeInteractiveActions);
        }
    };
    languageService.getEditsForRefactor = (filePath, formatOptions, positionOrRange, refactorName, actionName, preferences) => {
        let edits;
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, sourceScript, map] = (0, utils_1.getServiceScript)(language, fileName);
        if (serviceScript) {
            const generatePosition = (0, transform_1.toGeneratedOffset)(sourceScript, map, typeof positionOrRange === 'number'
                ? positionOrRange
                : positionOrRange.pos, language_core_1.isCodeActionsEnabled);
            if (generatePosition !== undefined) {
                const por = typeof positionOrRange === 'number'
                    ? generatePosition
                    : {
                        pos: generatePosition,
                        end: generatePosition + positionOrRange.end - positionOrRange.pos,
                    };
                edits = getEditsForRefactor(fileName, formatOptions, por, refactorName, actionName, preferences);
            }
        }
        else {
            edits = getEditsForRefactor(fileName, formatOptions, positionOrRange, refactorName, actionName, preferences);
        }
        if (edits) {
            edits.edits = edits.edits
                .map(edit => (0, transform_1.transformFileTextChanges)(language, edit, language_core_1.isCodeActionsEnabled))
                .filter(utils_1.notEmpty);
            return edits;
        }
    };
    languageService.getRenameInfo = (filePath, position, options) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, sourceScript, map] = (0, utils_1.getServiceScript)(language, fileName);
        if (serviceScript) {
            let failed;
            for (const [generateOffset, mapping] of (0, transform_1.toGeneratedOffsets)(sourceScript, map, position)) {
                if (!(0, language_core_1.isRenameEnabled)(mapping.data)) {
                    continue;
                }
                const info = getRenameInfo(fileName, generateOffset, options);
                if (info.canRename) {
                    const span = (0, transform_1.transformTextSpan)(sourceScript, map, info.triggerSpan, language_core_1.isRenameEnabled);
                    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);
        }
    };
    languageService.getCodeFixesAtPosition = (filePath, start, end, errorCodes, formatOptions, preferences) => {
        let fixes = [];
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, sourceScript, map] = (0, utils_1.getServiceScript)(language, fileName);
        if (serviceScript) {
            const generateStart = (0, transform_1.toGeneratedOffset)(sourceScript, map, start, language_core_1.isCodeActionsEnabled);
            const generateEnd = (0, transform_1.toGeneratedOffset)(sourceScript, map, end, language_core_1.isCodeActionsEnabled);
            if (generateStart !== undefined && generateEnd !== undefined) {
                fixes = getCodeFixesAtPosition(fileName, generateStart, generateEnd, errorCodes, formatOptions, preferences);
            }
        }
        else {
            fixes = getCodeFixesAtPosition(fileName, start, end, errorCodes, formatOptions, preferences);
        }
        fixes = fixes.map(fix => {
            fix.changes = fix.changes.map(edit => (0, transform_1.transformFileTextChanges)(language, edit, language_core_1.isCodeActionsEnabled)).filter(utils_1.notEmpty);
            return fix;
        });
        return fixes;
    };
    languageService.getEncodedSemanticClassifications = (filePath, span, format) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, sourceScript, map] = (0, utils_1.getServiceScript)(language, fileName);
        if (serviceScript) {
            let start;
            let end;
            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.lengths[mapping.lengths.length - 1];
                    start = Math.min(start, mapping.generatedOffsets[0]);
                    end = Math.max(end, mapping.generatedOffsets[mapping.generatedOffsets.length - 1] + mapping.lengths[mapping.lengths.length - 1]);
                }
            }
            start ??= 0;
            end ??= sourceScript.snapshot.getLength();
            start += sourceScript.snapshot.getLength();
            end += sourceScript.snapshot.getLength();
            const result = getEncodedSemanticClassifications(fileName, { start, length: end - start }, format);
            const spans = [];
            for (let i = 0; i < result.spans.length; i += 3) {
                const sourceStart = (0, transform_1.toSourceOffset)(sourceScript, map, result.spans[i], language_core_1.isSemanticTokensEnabled);
                const sourceEnd = (0, transform_1.toSourceOffset)(sourceScript, map, result.spans[i] + result.spans[i + 1], language_core_1.isSemanticTokensEnabled);
                if (sourceStart !== undefined && sourceEnd !== undefined) {
                    spans.push(sourceStart, sourceEnd - sourceStart, result.spans[i + 2]);
                }
            }
            result.spans = spans;
            return result;
        }
        else {
            return getEncodedSemanticClassifications(fileName, span, format);
        }
    };
    languageService.getSyntacticDiagnostics = filePath => {
        const fileName = filePath.replace(windowsPathReg, '/');
        return getSyntacticDiagnostics(fileName)
            .map(d => (0, transform_1.transformDiagnostic)(language, d, false))
            .filter(utils_1.notEmpty);
    };
    languageService.getSemanticDiagnostics = filePath => {
        const fileName = filePath.replace(windowsPathReg, '/');
        return getSemanticDiagnostics(fileName)
            .map(d => (0, transform_1.transformDiagnostic)(language, d, false))
            .filter(utils_1.notEmpty);
    };
    languageService.getSuggestionDiagnostics = filePath => {
        const fileName = filePath.replace(windowsPathReg, '/');
        return getSuggestionDiagnostics(fileName)
            .map(d => (0, transform_1.transformDiagnostic)(language, d, false))
            .filter(utils_1.notEmpty);
    };
    languageService.getDefinitionAndBoundSpan = (filePath, position) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const unresolved = linkedCodeFeatureWorker(fileName, position, language_core_1.isDefinitionEnabled, 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(utils_1.notEmpty)[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(utils_1.notEmpty))
            .filter(utils_1.notEmpty)
            .flat();
        return {
            textSpan,
            definitions: (0, dedupe_1.dedupeDocumentSpans)(definitions),
        };
    };
    languageService.findReferences = (filePath, position) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const unresolved = linkedCodeFeatureWorker(fileName, position, language_core_1.isReferencesEnabled, 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(utils_1.notEmpty),
            };
        });
        return resolved;
    };
    languageService.getDefinitionAtPosition = (filePath, position) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const unresolved = linkedCodeFeatureWorker(fileName, position, language_core_1.isDefinitionEnabled, 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(utils_1.notEmpty);
        return (0, dedupe_1.dedupeDocumentSpans)(resolved);
    };
    languageService.getTypeDefinitionAtPosition = (filePath, position) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const unresolved = linkedCodeFeatureWorker(fileName, position, language_core_1.isTypeDefinitionEnabled, 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(utils_1.notEmpty);
        return (0, dedupe_1.dedupeDocumentSpans)(resolved);
    };
    languageService.getImplementationAtPosition = (filePath, position) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const unresolved = linkedCodeFeatureWorker(fileName, position, language_core_1.isImplementationEnabled, 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(utils_1.notEmpty);
        return (0, dedupe_1.dedupeDocumentSpans)(resolved);
    };
    languageService.findRenameLocations = (filePath, position, findInStrings, findInComments, preferences) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const unresolved = linkedCodeFeatureWorker(fileName, position, language_core_1.isRenameEnabled, 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(utils_1.notEmpty);
        return (0, dedupe_1.dedupeDocumentSpans)(resolved);
    };
    languageService.getReferencesAtPosition = (filePath, position) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const unresolved = linkedCodeFeatureWorker(fileName, position, language_core_1.isReferencesEnabled, 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(utils_1.notEmpty);
        return (0, dedupe_1.dedupeDocumentSpans)(resolved);
    };
    languageService.getCompletionsAtPosition = (filePath, position, options, formattingSettings) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, sourceScript, map] = (0, utils_1.getServiceScript)(language, fileName);
        if (serviceScript) {
            const results = [];
            for (const [generatedOffset, mapping] of (0, transform_1.toGeneratedOffsets)(sourceScript, map, position)) {
                if (!(0, language_core_1.isCompletionEnabled)(mapping.data)) {
                    continue;
                }
                const result = getCompletionsAtPosition(fileName, 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, map, entry.replacementSpan, language_core_1.isCompletionEnabled);
                }
                result.optionalReplacementSpan = result.optionalReplacementSpan
                    && (0, transform_1.transformTextSpan)(sourceScript, map, result.optionalReplacementSpan, language_core_1.isCompletionEnabled);
                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);
        }
    };
    languageService.getCompletionEntryDetails = (filePath, position, entryName, formatOptions, source, preferences, data) => {
        let details;
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, sourceScript, map] = (0, utils_1.getServiceScript)(language, fileName);
        if (serviceScript) {
            const generatePosition = (0, transform_1.toGeneratedOffset)(sourceScript, map, position, language_core_1.isCompletionEnabled);
            if (generatePosition !== undefined) {
                details = getCompletionEntryDetails(fileName, 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 = codeAction.changes.map(edit => (0, transform_1.transformFileTextChanges)(language, edit, language_core_1.isCompletionEnabled)).filter(utils_1.notEmpty);
            }
        }
        return details;
    };
    languageService.provideInlayHints = (filePath, span, preferences) => {
        const fileName = filePath.replace(windowsPathReg, '/');
        const [serviceScript, sourceScript, map] = (0, utils_1.getServiceScript)(language, fileName);
        if (serviceScript) {
            let start;
            let end;
            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;
            }
            start += sourceScript.snapshot.getLength();
            end += sourceScript.snapshot.getLength();
            const result = provideInlayHints(fileName, { start, length: end - start }, preferences);
            const hints = [];
            for (const hint of result) {
                const sourcePosition = (0, transform_1.toSourceOffset)(sourceScript, map, hint.position, language_core_1.isInlayHintsEnabled);
                if (sourcePosition !== undefined) {
                    hints.push({
                        ...hint,
                        position: sourcePosition,
                    });
                }
            }
            return hints;
        }
        else {
            return provideInlayHints(fileName, span, preferences);
        }
    };
    languageService.getFileReferences = 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(utils_1.notEmpty);
        return (0, dedupe_1.dedupeDocumentSpans)(resolved);
    };
    function linkedCodeFeatureWorker(fileName, position, filter, worker, getLinkedCodes) {
        const results = [];
        const processedFilePositions = new Set();
        const [serviceScript, sourceScript, map] = (0, utils_1.getServiceScript)(language, fileName);
        if (serviceScript) {
            for (const [generatedOffset, mapping] of map.getGeneratedOffsets(position)) {
                if (filter(mapping.data)) {
                    process(fileName, generatedOffset + sourceScript.snapshot.getLength());
                }
            }
        }
        else {
            process(fileName, position);
        }
        return results;
        function process(fileName, position) {
            if (processedFilePositions.has(fileName + ':' + position)) {
                return;
            }
            processedFilePositions.add(fileName + ':' + position);
            const result = worker(position);
            if (!result) {
                return;
            }
            results.push(result);
            for (const ref of getLinkedCodes(result)) {
                processedFilePositions.add(ref[0] + ':' + ref[1]);
                const [virtualFile, sourceScript] = (0, utils_1.getServiceScript)(language, ref[0]);
                if (!virtualFile) {
                    continue;
                }
                const linkedCodeMap = language.linkedCodeMaps.get(virtualFile.code);
                if (!linkedCodeMap) {
                    continue;
                }
                for (const linkedCodeOffset of linkedCodeMap.getLinkedOffsets(ref[1] - sourceScript.snapshot.getLength())) {
                    process(ref[0], linkedCodeOffset + sourceScript.snapshot.getLength());
                }
            }
        }
    }
}
exports.decorateLanguageService = decorateLanguageService;
function displayPartsToString(displayParts) {
    if (displayParts) {
        return displayParts.map(displayPart => displayPart.text).join('');
    }
    return '';
}
//# sourceMappingURL=decorateLanguageService.js.map