monaco-editor
Version:
A browser based code editor
207 lines (206 loc) • 9.5 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* 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 PostEditWidget_1;
import * as dom from '../../../../base/browser/dom.js';
import { Button } from '../../../../base/browser/ui/button/button.js';
import { toAction } from '../../../../base/common/actions.js';
import { toErrorMessage } from '../../../../base/common/errorMessage.js';
import { isCancellationError } from '../../../../base/common/errors.js';
import { Event } from '../../../../base/common/event.js';
import { Disposable, MutableDisposable, toDisposable } from '../../../../base/common/lifecycle.js';
import './postEditWidget.css';
import { IBulkEditService } from '../../../browser/services/bulkEditService.js';
import { createCombinedWorkspaceEdit } from './edit.js';
import { localize } from '../../../../nls.js';
import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js';
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js';
import { INotificationService } from '../../../../platform/notification/common/notification.js';
let PostEditWidget = class PostEditWidget extends Disposable {
static { PostEditWidget_1 = this; }
static { this.baseId = 'editor.widget.postEditWidget'; }
constructor(typeId, editor, visibleContext, showCommand, range, edits, onSelectNewEdit, _contextMenuService, contextKeyService, _keybindingService) {
super();
this.typeId = typeId;
this.editor = editor;
this.showCommand = showCommand;
this.range = range;
this.edits = edits;
this.onSelectNewEdit = onSelectNewEdit;
this._contextMenuService = _contextMenuService;
this._keybindingService = _keybindingService;
this.allowEditorOverflow = true;
this.suppressMouseDown = true;
this.create();
this.visibleContext = visibleContext.bindTo(contextKeyService);
this.visibleContext.set(true);
this._register(toDisposable(() => this.visibleContext.reset()));
this.editor.addContentWidget(this);
this.editor.layoutContentWidget(this);
this._register(toDisposable((() => this.editor.removeContentWidget(this))));
this._register(this.editor.onDidChangeCursorPosition(e => {
if (!range.containsPosition(e.position)) {
this.dispose();
}
}));
this._register(Event.runAndSubscribe(_keybindingService.onDidUpdateKeybindings, () => {
this._updateButtonTitle();
}));
}
_updateButtonTitle() {
const binding = this._keybindingService.lookupKeybinding(this.showCommand.id)?.getLabel();
this.button.element.title = this.showCommand.label + (binding ? ` (${binding})` : '');
}
create() {
this.domNode = dom.$('.post-edit-widget');
this.button = this._register(new Button(this.domNode, {
supportIcons: true,
}));
this.button.label = '$(insert)';
this._register(dom.addDisposableListener(this.domNode, dom.EventType.CLICK, () => this.showSelector()));
}
getId() {
return PostEditWidget_1.baseId + '.' + this.typeId;
}
getDomNode() {
return this.domNode;
}
getPosition() {
return {
position: this.range.getEndPosition(),
preference: [2 /* ContentWidgetPositionPreference.BELOW */]
};
}
showSelector() {
this._contextMenuService.showContextMenu({
getAnchor: () => {
const pos = dom.getDomNodePagePosition(this.button.element);
return { x: pos.left + pos.width, y: pos.top + pos.height };
},
getActions: () => {
return this.edits.allEdits.map((edit, i) => toAction({
id: '',
label: edit.title,
checked: i === this.edits.activeEditIndex,
run: () => {
if (i !== this.edits.activeEditIndex) {
return this.onSelectNewEdit(i);
}
},
}));
}
});
}
};
PostEditWidget = PostEditWidget_1 = __decorate([
__param(7, IContextMenuService),
__param(8, IContextKeyService),
__param(9, IKeybindingService)
], PostEditWidget);
let PostEditWidgetManager = class PostEditWidgetManager extends Disposable {
constructor(_id, _editor, _visibleContext, _showCommand, _instantiationService, _bulkEditService, _notificationService) {
super();
this._id = _id;
this._editor = _editor;
this._visibleContext = _visibleContext;
this._showCommand = _showCommand;
this._instantiationService = _instantiationService;
this._bulkEditService = _bulkEditService;
this._notificationService = _notificationService;
this._currentWidget = this._register(new MutableDisposable());
this._register(Event.any(_editor.onDidChangeModel, _editor.onDidChangeModelContent)(() => this.clear()));
}
async applyEditAndShowIfNeeded(ranges, edits, canShowWidget, resolve, token) {
const model = this._editor.getModel();
if (!model || !ranges.length) {
return;
}
const edit = edits.allEdits.at(edits.activeEditIndex);
if (!edit) {
return;
}
const onDidSelectEdit = async (newEditIndex) => {
const model = this._editor.getModel();
if (!model) {
return;
}
await model.undo();
this.applyEditAndShowIfNeeded(ranges, { activeEditIndex: newEditIndex, allEdits: edits.allEdits }, canShowWidget, resolve, token);
};
const handleError = (e, message) => {
if (isCancellationError(e)) {
return;
}
this._notificationService.error(message);
if (canShowWidget) {
this.show(ranges[0], edits, onDidSelectEdit);
}
};
let resolvedEdit;
try {
resolvedEdit = await resolve(edit, token);
}
catch (e) {
return handleError(e, localize('resolveError', "Error resolving edit '{0}':\n{1}", edit.title, toErrorMessage(e)));
}
if (token.isCancellationRequested) {
return;
}
const combinedWorkspaceEdit = createCombinedWorkspaceEdit(model.uri, ranges, resolvedEdit);
// Use a decoration to track edits around the trigger range
const primaryRange = ranges[0];
const editTrackingDecoration = model.deltaDecorations([], [{
range: primaryRange,
options: { description: 'paste-line-suffix', stickiness: 0 /* TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges */ }
}]);
this._editor.focus();
let editResult;
let editRange;
try {
editResult = await this._bulkEditService.apply(combinedWorkspaceEdit, { editor: this._editor, token });
editRange = model.getDecorationRange(editTrackingDecoration[0]);
}
catch (e) {
return handleError(e, localize('applyError', "Error applying edit '{0}':\n{1}", edit.title, toErrorMessage(e)));
}
finally {
model.deltaDecorations(editTrackingDecoration, []);
}
if (token.isCancellationRequested) {
return;
}
if (canShowWidget && editResult.isApplied && edits.allEdits.length > 1) {
this.show(editRange ?? primaryRange, edits, onDidSelectEdit);
}
}
show(range, edits, onDidSelectEdit) {
this.clear();
if (this._editor.hasModel()) {
this._currentWidget.value = this._instantiationService.createInstance((PostEditWidget), this._id, this._editor, this._visibleContext, this._showCommand, range, edits, onDidSelectEdit);
}
}
clear() {
this._currentWidget.clear();
}
tryShowSelector() {
this._currentWidget.value?.showSelector();
}
};
PostEditWidgetManager = __decorate([
__param(4, IInstantiationService),
__param(5, IBulkEditService),
__param(6, INotificationService)
], PostEditWidgetManager);
export { PostEditWidgetManager };