"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CompletionAdapter = void 0;
const tslib_1 = require("tslib");
const ide_core_common_1 = require("@opensumi/ide-core-common");
const vscode_1 = require("../../../../common/vscode");
const Converter = tslib_1.__importStar(require("../../../../common/vscode/converter"));
const ext_types_1 = require("../../../../common/vscode/ext-types");
const model_api_1 = require("../../../../common/vscode/model.api");
const util_1 = require("./util");
class CompletionAdapter {
    static supportsResolving(provider) {
        return typeof provider.resolveCompletionItem === 'function';
    }
    constructor(delegate, commandConverter, documents) {
        this.delegate = delegate;
        this.commandConverter = commandConverter;
        this.documents = documents;
        this.cache = new ide_core_common_1.Cache('CompletionItem');
        this.toDispose = new Map();
    }
    async provideCompletionItems(resource, position, context, token) {
        const { doc, pos, replacing, inserting } = await this.getInsertOrReplace(resource, position);
        const perf = (0, util_1.getPerformance)();
        const startTime = perf ? perf.now() : 0;
        const itemOrList = await this.delegate.provideCompletionItems(doc, pos, token, context);
        const duration = perf ? Math.round(perf.now() - startTime) : 0;
        if (!itemOrList) {
            return undefined;
        }
        const isIncomplete = Array.isArray(itemOrList) ? false : itemOrList.isIncomplete;
        const list = Array.isArray(itemOrList) ? new ext_types_1.CompletionList(itemOrList) : itemOrList;
        const pid = CompletionAdapter.supportsResolving(this.delegate)
            ? this.cache.add(list.items.map((e) => ({ item: e, resource, position })))
            : this.cache.add([]);
        const disposables = new ide_core_common_1.DisposableStore();
        this.toDispose.set(pid, disposables);
        const completions = [];
        const result = {
            x: pid,
            [vscode_1.ISuggestResultDtoField.completions]: completions,
            [vscode_1.ISuggestResultDtoField.defaultRanges]: {
                replace: Converter.Range.from(replacing),
                insert: Converter.Range.from(inserting),
            },
            [vscode_1.ISuggestResultDtoField.isIncomplete]: isIncomplete || undefined,
            [vscode_1.ISuggestResultDtoField.duration]: duration,
        };
        for (let i = 0; i < list.items.length; i++) {
            const item = list.items[i];
            // check for bad completion item first
            const dto = this.convertCompletionItem(item, [pid, i], inserting, replacing);
            completions.push(dto);
        }
        return result;
    }
    async resolveCompletionItem(id, token) {
        const _cache = this.cache.get(...id);
        if (!_cache) {
            return undefined;
        }
        const { item, resource, position } = _cache;
        let inserting;
        let replacing;
        if (resource && position) {
            const _ = await this.getInsertOrReplace(resource, position);
            inserting = _.inserting;
            replacing = _.replacing;
        }
        const convertItem = this.convertCompletionItem(item, id, inserting, replacing);
        if (typeof this.delegate.resolveCompletionItem !== 'function') {
            return convertItem;
        }
        try {
            const resolvedItem = await this.delegate.resolveCompletionItem(item, token);
            if (!resolvedItem) {
                return convertItem;
            }
            return this.convertCompletionItem(resolvedItem, id);
        }
        catch (error) {
            new Error(`Delegate.resolveCompletionItem error: ${error}`);
        }
        return convertItem;
    }
    async releaseCompletionItems(id) {
        this.cache.delete(id);
        const toDispose = this.toDispose.get(id);
        if (toDispose) {
            toDispose.dispose();
            this.toDispose.delete(id);
        }
        return Promise.resolve();
    }
    async getInsertOrReplace(resource, position) {
        const document = this.documents.getDocumentData(resource);
        if (!document) {
            return Promise.reject(new Error(`There are no document for  ${resource}`));
        }
        const doc = document.document;
        const pos = Converter.toPosition(position);
        const replacing = doc.getWordRangeAtPosition(pos) || new ext_types_1.Range(pos, pos);
        const inserting = replacing.with({ end: pos });
        return { inserting, replacing, doc, pos };
    }
    convertRange(item, defaultInserting, defaultReplacing) {
        let range;
        if (item.textEdit) {
            range = item.textEdit.range;
        }
        else if (item.range) {
            range = item.range;
        }
        let toRange;
        if (ext_types_1.Range.isRange(range)) {
            toRange = vscode_1.RangeSuggestDataDto.to(Converter.Range.from(range));
        }
        else if (range && (!(defaultInserting === null || defaultInserting === void 0 ? void 0 : defaultInserting.isEqual(range.inserting)) || !(defaultReplacing === null || defaultReplacing === void 0 ? void 0 : defaultReplacing.isEqual(range.replacing)))) {
            toRange = {
                insert: Converter.Range.from(range.inserting),
                replace: Converter.Range.from(range.replacing),
            };
        }
        return toRange;
    }
    convertCompletionLabel(label) {
        let labelStr = label.label;
        if (label.description) {
            labelStr += `~|${label.description}`;
        }
        if (label.detail) {
            labelStr += `~|${label.detail}`;
        }
        return labelStr;
    }
    convertCompletionItem(item, id, defaultInserting, defaultReplacing) {
        const disposables = this.toDispose.get(id[0]);
        if (!disposables) {
            throw Error('DisposableStore is missing...');
        }
        const result = {
            x: id,
            [vscode_1.ISuggestDataDtoField.kind]: item.kind ? Converter.CompletionItemKind.from(item.kind) : undefined,
            [vscode_1.ISuggestDataDtoField.kindModifier]: item.tags && item.tags.map(Converter.CompletionItemTag.from),
            [vscode_1.ISuggestDataDtoField.label]: typeof item.label === 'string' ? item.label : this.convertCompletionLabel(item.label),
            [vscode_1.ISuggestDataDtoField.detail]: item.detail,
            [vscode_1.ISuggestDataDtoField.documentation]: item.documentation,
            [vscode_1.ISuggestDataDtoField.filterText]: item.filterText,
            [vscode_1.ISuggestDataDtoField.sortText]: item.sortText,
            [vscode_1.ISuggestDataDtoField.preselect]: item.preselect ? item.preselect : undefined,
            [vscode_1.ISuggestDataDtoField.insertText]: '',
            [vscode_1.ISuggestDataDtoField.additionalTextEdits]: item.additionalTextEdits && item.additionalTextEdits.map(Converter.fromTextEdit),
            [vscode_1.ISuggestDataDtoField.command]: this.commandConverter.toInternal(item.command, disposables),
            [vscode_1.ISuggestDataDtoField.commitCharacters]: item.commitCharacters,
            [vscode_1.ISuggestDataDtoField.insertTextRules]: item.keepWhitespace
                ? model_api_1.CompletionItemInsertTextRule.KeepWhitespace
                : model_api_1.CompletionItemInsertTextRule.None,
        };
        const convertRange = this.convertRange(item, defaultInserting, defaultReplacing);
        if (convertRange) {
            result[vscode_1.ISuggestDataDtoField.range] = convertRange;
        }
        if (item.textEdit) {
            result[vscode_1.ISuggestDataDtoField.insertText] = item.textEdit.newText;
        }
        else if (typeof item.insertText === 'string') {
            result[vscode_1.ISuggestDataDtoField.insertText] = item.insertText;
        }
        else if (item.insertText instanceof ext_types_1.SnippetString) {
            result[vscode_1.ISuggestDataDtoField.insertText] = item.insertText.value;
            result[vscode_1.ISuggestDataDtoField.insertTextRules] = model_api_1.CompletionItemInsertTextRule.InsertAsSnippet;
        }
        else {
            result[vscode_1.ISuggestDataDtoField.insertText] = typeof item.label === 'string' ? item.label : item.label.label;
        }
        return result;
    }
    static hasResolveSupport(provider) {
        return typeof provider.resolveCompletionItem === 'function';
    }
}
exports.CompletionAdapter = CompletionAdapter;
//# sourceMappingURL=completion.js.map