UNPKG

monaco-editor

Version:
169 lines (168 loc) • 9.64 kB
/*--------------------------------------------------------------------------------------------- * 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 DropIntoEditorController_1; import { coalesce } from '../../../../base/common/arrays.js'; import { createCancelablePromise, raceCancellation } from '../../../../base/common/async.js'; import { VSDataTransfer, matchesMimeType } from '../../../../base/common/dataTransfer.js'; import { HierarchicalKind } from '../../../../base/common/hierarchicalKind.js'; import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; import { toExternalVSDataTransfer } from '../../../browser/dnd.js'; import { Range } from '../../../common/core/range.js'; import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; import { DraggedTreeItemsIdentifier } from '../../../common/services/treeViewsDnd.js'; import { ITreeViewsDnDService } from '../../../common/services/treeViewsDndService.js'; import { EditorStateCancellationTokenSource } from '../../editorState/browser/editorState.js'; import { InlineProgressManager } from '../../inlineProgress/browser/inlineProgress.js'; import { localize } from '../../../../nls.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; import { LocalSelectionTransfer } from '../../../../platform/dnd/browser/dnd.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { sortEditsByYieldTo } from './edit.js'; import { PostEditWidgetManager } from './postEditWidget.js'; export const defaultProviderConfig = 'editor.experimental.dropIntoEditor.defaultProvider'; export const changeDropTypeCommandId = 'editor.changeDropType'; export const dropWidgetVisibleCtx = new RawContextKey('dropWidgetVisible', false, localize('dropWidgetVisible', "Whether the drop widget is showing")); let DropIntoEditorController = class DropIntoEditorController extends Disposable { static { DropIntoEditorController_1 = this; } static { this.ID = 'editor.contrib.dropIntoEditorController'; } static get(editor) { return editor.getContribution(DropIntoEditorController_1.ID); } constructor(editor, instantiationService, _configService, _languageFeaturesService, _treeViewsDragAndDropService) { super(); this._configService = _configService; this._languageFeaturesService = _languageFeaturesService; this._treeViewsDragAndDropService = _treeViewsDragAndDropService; this.treeItemsTransfer = LocalSelectionTransfer.getInstance(); this._dropProgressManager = this._register(instantiationService.createInstance(InlineProgressManager, 'dropIntoEditor', editor)); this._postDropWidgetManager = this._register(instantiationService.createInstance(PostEditWidgetManager, 'dropIntoEditor', editor, dropWidgetVisibleCtx, { id: changeDropTypeCommandId, label: localize('postDropWidgetTitle', "Show drop options...") })); this._register(editor.onDropIntoEditor(e => this.onDropIntoEditor(editor, e.position, e.event))); } clearWidgets() { this._postDropWidgetManager.clear(); } changeDropType() { this._postDropWidgetManager.tryShowSelector(); } async onDropIntoEditor(editor, position, dragEvent) { if (!dragEvent.dataTransfer || !editor.hasModel()) { return; } this._currentOperation?.cancel(); editor.focus(); editor.setPosition(position); const p = createCancelablePromise(async (token) => { const disposables = new DisposableStore(); const tokenSource = disposables.add(new EditorStateCancellationTokenSource(editor, 1 /* CodeEditorStateFlag.Value */, undefined, token)); try { const ourDataTransfer = await this.extractDataTransferData(dragEvent); if (ourDataTransfer.size === 0 || tokenSource.token.isCancellationRequested) { return; } const model = editor.getModel(); if (!model) { return; } const providers = this._languageFeaturesService.documentDropEditProvider .ordered(model) .filter(provider => { if (!provider.dropMimeTypes) { // Keep all providers that don't specify mime types return true; } return provider.dropMimeTypes.some(mime => ourDataTransfer.matches(mime)); }); const editSession = disposables.add(await this.getDropEdits(providers, model, position, ourDataTransfer, tokenSource)); if (tokenSource.token.isCancellationRequested) { return; } if (editSession.edits.length) { const activeEditIndex = this.getInitialActiveEditIndex(model, editSession.edits); const canShowWidget = editor.getOption(36 /* EditorOption.dropIntoEditor */).showDropSelector === 'afterDrop'; // Pass in the parent token here as it tracks cancelling the entire drop operation await this._postDropWidgetManager.applyEditAndShowIfNeeded([Range.fromPositions(position)], { activeEditIndex, allEdits: editSession.edits }, canShowWidget, async (edit) => edit, token); } } finally { disposables.dispose(); if (this._currentOperation === p) { this._currentOperation = undefined; } } }); this._dropProgressManager.showWhile(position, localize('dropIntoEditorProgress', "Running drop handlers. Click to cancel"), p, { cancel: () => p.cancel() }); this._currentOperation = p; } async getDropEdits(providers, model, position, dataTransfer, tokenSource) { const disposables = new DisposableStore(); const results = await raceCancellation(Promise.all(providers.map(async (provider) => { try { const edits = await provider.provideDocumentDropEdits(model, position, dataTransfer, tokenSource.token); if (edits) { disposables.add(edits); } return edits?.edits.map(edit => ({ ...edit, providerId: provider.id })); } catch (err) { console.error(err); } return undefined; })), tokenSource.token); const edits = coalesce(results ?? []).flat(); return { edits: sortEditsByYieldTo(edits), dispose: () => disposables.dispose() }; } getInitialActiveEditIndex(model, edits) { const preferredProviders = this._configService.getValue(defaultProviderConfig, { resource: model.uri }); for (const [configMime, desiredKindStr] of Object.entries(preferredProviders)) { const desiredKind = new HierarchicalKind(desiredKindStr); const editIndex = edits.findIndex(edit => desiredKind.value === edit.providerId && edit.handledMimeType && matchesMimeType(configMime, [edit.handledMimeType])); if (editIndex >= 0) { return editIndex; } } return 0; } async extractDataTransferData(dragEvent) { if (!dragEvent.dataTransfer) { return new VSDataTransfer(); } const dataTransfer = toExternalVSDataTransfer(dragEvent.dataTransfer); if (this.treeItemsTransfer.hasData(DraggedTreeItemsIdentifier.prototype)) { const data = this.treeItemsTransfer.getData(DraggedTreeItemsIdentifier.prototype); if (Array.isArray(data)) { for (const id of data) { const treeDataTransfer = await this._treeViewsDragAndDropService.removeDragOperationTransfer(id.identifier); if (treeDataTransfer) { for (const [type, value] of treeDataTransfer) { dataTransfer.replace(type, value); } } } } } return dataTransfer; } }; DropIntoEditorController = DropIntoEditorController_1 = __decorate([ __param(1, IInstantiationService), __param(2, IConfigurationService), __param(3, ILanguageFeaturesService), __param(4, ITreeViewsDnDService) ], DropIntoEditorController); export { DropIntoEditorController };