/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import { coalesce } from '../../../../base/common/arrays.js';
import { createCancelablePromise, raceCancellation } from '../../../../base/common/async.js';
import { VSDataTransfer } from '../../../../base/common/dataTransfer.js';
import { Disposable } from '../../../../base/common/lifecycle.js';
import { addExternalEditorsDropData, toVSDataTransfer } from '../../../browser/dnd.js';
import { registerEditorContribution } from '../../../browser/editorExtensions.js';
import { IBulkEditService, ResourceTextEdit } from '../../../browser/services/bulkEditService.js';
import { Range } from '../../../common/core/range.js';
import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js';
import { PostDropWidgetManager } from './postDropWidget.js';
import { EditorStateCancellationTokenSource } from '../../editorState/browser/editorState.js';
import { SnippetParser } from '../../snippet/browser/snippetParser.js';
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js';
import { registerDefaultDropProviders } from './defaultOnDropProviders.js';
import { InlineProgressManager } from '../../inlineProgress/browser/inlineProgress.js';
import { localize } from '../../../../nls.js';
let DropIntoEditorController = class DropIntoEditorController extends Disposable {
    constructor(editor, _bulkEditService, _languageFeaturesService, instantiationService, workspaceContextService) {
        super();
        this._bulkEditService = _bulkEditService;
        this._languageFeaturesService = _languageFeaturesService;
        this.operationIdPool = 0;
        this._dropProgressManager = this._register(new InlineProgressManager('dropIntoEditor', editor, instantiationService));
        this._postDropWidgetManager = this._register(new PostDropWidgetManager(editor, instantiationService));
        this._register(editor.onDropIntoEditor(e => this.onDropIntoEditor(editor, e.position, e.event)));
        registerDefaultDropProviders(this._languageFeaturesService, workspaceContextService);
    }
    onDropIntoEditor(editor, position, dragEvent) {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            if (!dragEvent.dataTransfer || !editor.hasModel()) {
                return;
            }
            (_a = this._currentOperation) === null || _a === void 0 ? void 0 : _a.promise.cancel();
            this._dropProgressManager.clear();
            editor.focus();
            editor.setPosition(position);
            const operationId = this.operationIdPool++;
            const p = createCancelablePromise((token) => __awaiter(this, void 0, void 0, function* () {
                var _b;
                const tokenSource = new EditorStateCancellationTokenSource(editor, 1 /* CodeEditorStateFlag.Value */, undefined, token);
                this._dropProgressManager.setAtPosition(position, localize('dropIntoEditorProgress', "Running drop handlers. Click to cancel"), {
                    cancel: () => tokenSource.cancel()
                });
                try {
                    const ourDataTransfer = yield this.extractDataTransferData(dragEvent);
                    if (ourDataTransfer.size === 0 || tokenSource.token.isCancellationRequested) {
                        return;
                    }
                    const model = editor.getModel();
                    if (!model) {
                        return;
                    }
                    const providers = this._languageFeaturesService.documentOnDropEditProvider
                        .ordered(model)
                        .filter(provider => {
                        if (!provider.dropMimeTypes) {
                            // Keep all providers that don't specify mime types
                            return true;
                        }
                        return provider.dropMimeTypes.some(mime => ourDataTransfer.has(mime));
                    });
                    const possibleDropEdits = yield raceCancellation(Promise.all(providers.map(provider => {
                        return provider.provideDocumentOnDropEdits(model, position, ourDataTransfer, tokenSource.token);
                    })), tokenSource.token);
                    if (tokenSource.token.isCancellationRequested) {
                        return;
                    }
                    if (possibleDropEdits) {
                        // Pass in the parent token here as it tracks cancelling the entire drop operation.
                        yield this.applyDropResult(editor, position, 0, coalesce(possibleDropEdits), token);
                    }
                }
                finally {
                    tokenSource.dispose();
                    if (((_b = this._currentOperation) === null || _b === void 0 ? void 0 : _b.id) === operationId) {
                        this._dropProgressManager.clear();
                        this._currentOperation = undefined;
                    }
                }
            }));
            this._currentOperation = { id: operationId, promise: p };
        });
    }
    extractDataTransferData(dragEvent) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!dragEvent.dataTransfer) {
                return new VSDataTransfer();
            }
            const textEditorDataTransfer = toVSDataTransfer(dragEvent.dataTransfer);
            addExternalEditorsDropData(textEditorDataTransfer, dragEvent);
            return textEditorDataTransfer;
        });
    }
    applyDropResult(editor, position, selectedEditIndex, allEdits, token) {
        var _a, _b;
        return __awaiter(this, void 0, void 0, function* () {
            const model = editor.getModel();
            if (!model) {
                return;
            }
            const edit = allEdits[selectedEditIndex];
            if (!edit) {
                return;
            }
            const snippet = typeof edit.insertText === 'string' ? SnippetParser.escape(edit.insertText) : edit.insertText.snippet;
            const combinedWorkspaceEdit = {
                edits: [
                    new ResourceTextEdit(model.uri, {
                        range: Range.fromPositions(position),
                        text: snippet,
                        insertAsSnippet: true,
                    }),
                    ...((_b = (_a = edit.additionalEdit) === null || _a === void 0 ? void 0 : _a.edits) !== null && _b !== void 0 ? _b : [])
                ]
            };
            // Use a decoration to track edits around the cursor
            const editTrackingDecoration = model.deltaDecorations([], [{
                    range: Range.fromPositions(position),
                    options: { description: 'drop-line-suffix', stickiness: 0 /* TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges */ }
                }]);
            const editResult = yield this._bulkEditService.apply(combinedWorkspaceEdit, { editor, token });
            const editRange = model.getDecorationRange(editTrackingDecoration[0]);
            model.deltaDecorations(editTrackingDecoration, []);
            if (editResult.isApplied && allEdits.length > 1) {
                const options = editor.getOptions().get(34 /* EditorOption.dropIntoEditor */);
                if (options.showDropSelector === 'afterDrop') {
                    this._postDropWidgetManager.show(editRange !== null && editRange !== void 0 ? editRange : Range.fromPositions(position), {
                        activeEditIndex: selectedEditIndex,
                        allEdits: allEdits,
                    }, (newEditIndex) => __awaiter(this, void 0, void 0, function* () {
                        yield model.undo();
                        this.applyDropResult(editor, position, newEditIndex, allEdits, token);
                    }));
                }
            }
        });
    }
};
DropIntoEditorController.ID = 'editor.contrib.dropIntoEditorController';
DropIntoEditorController = __decorate([
    __param(1, IBulkEditService),
    __param(2, ILanguageFeaturesService),
    __param(3, IInstantiationService),
    __param(4, IWorkspaceContextService)
], DropIntoEditorController);
export { DropIntoEditorController };
registerEditorContribution(DropIntoEditorController.ID, DropIntoEditorController, 2 /* EditorContributionInstantiation.BeforeFirstInteraction */);
