@codingame/monaco-vscode-extensions-service-override
Version:
VSCode public API plugged on the monaco editor - extensions service-override
807 lines (803 loc) • 37.5 kB
JavaScript
import { __decorate, __param } from '@codingame/monaco-vscode-api/external/tslib/tslib.es6';
import { multibyteAwareBtoa } from '@codingame/monaco-vscode-api/vscode/vs/base/common/strings';
import { DeferredPromise, createCancelablePromise } from '@codingame/monaco-vscode-api/vscode/vs/base/common/async';
import { CancellationToken, CancellationTokenSource } from '@codingame/monaco-vscode-api/vscode/vs/base/common/cancellation';
import { onUnexpectedError, isCancellationError } from '@codingame/monaco-vscode-api/vscode/vs/base/common/errors';
import { Emitter, Event } from '@codingame/monaco-vscode-api/vscode/vs/base/common/event';
import { Disposable, DisposableMap, DisposableStore } from '@codingame/monaco-vscode-api/vscode/vs/base/common/lifecycle';
import { Schemas } from '@codingame/monaco-vscode-api/vscode/vs/base/common/network';
import { basename } from '@codingame/monaco-vscode-api/vscode/vs/base/common/path';
import { isEqualOrParent, isEqual, toLocalResource } from '@codingame/monaco-vscode-api/vscode/vs/base/common/resources';
import { URI } from '@codingame/monaco-vscode-api/vscode/vs/base/common/uri';
import { generateUuid } from '@codingame/monaco-vscode-api/vscode/vs/base/common/uuid';
import { localize } from '@codingame/monaco-vscode-api/vscode/vs/nls';
import { IFileDialogService } from '@codingame/monaco-vscode-api/vscode/vs/platform/dialogs/common/dialogs.service';
import { FileOperation } from '@codingame/monaco-vscode-api/vscode/vs/platform/files/common/files';
import { IFileService } from '@codingame/monaco-vscode-api/vscode/vs/platform/files/common/files.service';
import { IInstantiationService } from '@codingame/monaco-vscode-api/vscode/vs/platform/instantiation/common/instantiation';
import { ILabelService } from '@codingame/monaco-vscode-api/vscode/vs/platform/label/common/label.service';
import { IStorageService } from '@codingame/monaco-vscode-api/vscode/vs/platform/storage/common/storage.service';
import { UndoRedoElementType } from '@codingame/monaco-vscode-api/vscode/vs/platform/undoRedo/common/undoRedo';
import { IUndoRedoService } from '@codingame/monaco-vscode-api/vscode/vs/platform/undoRedo/common/undoRedo.service';
import { reviveWebviewExtension } from './mainThreadWebviews.js';
import { ExtHostContext } from '@codingame/monaco-vscode-api/vscode/vs/workbench/api/common/extHost.protocol';
import { CustomEditorDiffInput, CustomEditorSideBySideDiffInput } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/customEditor/browser/customEditorDiffInput';
import { CustomEditorInput } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/customEditor/browser/customEditorInput';
import { ICustomEditorService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/customEditor/common/customEditor.service';
import { CustomTextEditorModel } from '../../contrib/customEditor/common/customTextEditorModel.js';
import { ExtensionKeyedWebviewOriginStore } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/webview/browser/webview';
import { IWebviewWorkbenchService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService.service';
import { editorGroupToColumn } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/editor/common/editorGroupColumn';
import { IEditorGroupsService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/editor/common/editorGroupsService.service';
import { IEditorService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/editor/common/editorService.service';
import { IWorkbenchEnvironmentService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/environment/common/environmentService.service';
import { IExtensionService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/extensions/common/extensions.service';
import { IPathService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/path/common/pathService.service';
import { ResourceWorkingCopy } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/workingCopy/common/resourceWorkingCopy';
import { NO_TYPE_ID, WorkingCopyCapabilities } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/workingCopy/common/workingCopy';
import { IWorkingCopyFileService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/workingCopy/common/workingCopyFileService.service';
import { IWorkingCopyService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/workingCopy/common/workingCopyService.service';
import { IUriIdentityService } from '@codingame/monaco-vscode-api/vscode/vs/platform/uriIdentity/common/uriIdentity.service';
import { IUntitledTextEditorService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/untitled/common/untitledTextEditorService.service';
var MainThreadCustomEditorModel_1;
var CustomEditorModelType;
(function(CustomEditorModelType) {
CustomEditorModelType[CustomEditorModelType["Custom"] = 0] = "Custom";
CustomEditorModelType[CustomEditorModelType["Text"] = 1] = "Text";
})(CustomEditorModelType || (CustomEditorModelType = {}));
let MainThreadCustomEditors = class MainThreadCustomEditors extends Disposable {
constructor(
context,
mainThreadWebview,
mainThreadWebviewPanels,
extensionService,
storageService,
workingCopyService,
workingCopyFileService,
_customEditorService,
_editorGroupService,
_editorService,
_instantiationService,
_webviewWorkbenchService,
_uriIdentityService,
_untitledTextEditorService
) {
super();
this.mainThreadWebview = mainThreadWebview;
this.mainThreadWebviewPanels = mainThreadWebviewPanels;
this._customEditorService = _customEditorService;
this._editorGroupService = _editorGroupService;
this._editorService = _editorService;
this._instantiationService = _instantiationService;
this._webviewWorkbenchService = _webviewWorkbenchService;
this._uriIdentityService = _uriIdentityService;
this._untitledTextEditorService = _untitledTextEditorService;
this._editorProviders = this._register(( new DisposableMap()));
this._editorRenameBackups = ( new Map());
this._pendingSideBySideDiffResolutions = ( new Map());
this._webviewOriginStore = ( new ExtensionKeyedWebviewOriginStore("mainThreadCustomEditors.origins", storageService));
this._proxyCustomEditors = ( context.getProxy(ExtHostContext.ExtHostCustomEditors));
this._register(workingCopyFileService.registerWorkingCopyProvider(editorResource => {
const matchedWorkingCopies = [];
for (const workingCopy of workingCopyService.workingCopies) {
if (workingCopy instanceof MainThreadCustomEditorModel) {
if (isEqualOrParent(editorResource, workingCopy.editorResource)) {
matchedWorkingCopies.push(workingCopy);
}
}
}
return matchedWorkingCopies;
}));
this._register(_webviewWorkbenchService.registerResolver({
canResolve: webview => {
if (webview instanceof CustomEditorInput || webview instanceof CustomEditorDiffInput || webview instanceof CustomEditorSideBySideDiffInput) {
extensionService.activateByEvent(`onCustomEditor:${webview.viewType}`);
}
return false;
},
resolveWebview: () => {
throw ( new Error("not implemented"));
}
}));
this._register(
workingCopyFileService.onWillRunWorkingCopyFileOperation(async e => this.onWillRunWorkingCopyFileOperation(e))
);
}
$registerTextEditorProvider(
extensionData,
viewType,
options,
capabilities,
serializeBuffersForPostMessage
) {
this.registerEditorProvider(
CustomEditorModelType.Text,
reviveWebviewExtension(extensionData),
viewType,
options,
capabilities,
true,
serializeBuffersForPostMessage
);
}
$registerCustomEditorProvider(
extensionData,
viewType,
options,
capabilities,
supportsMultipleEditorsPerDocument,
serializeBuffersForPostMessage
) {
this.registerEditorProvider(
CustomEditorModelType.Custom,
reviveWebviewExtension(extensionData),
viewType,
options,
capabilities,
supportsMultipleEditorsPerDocument,
serializeBuffersForPostMessage
);
}
registerEditorProvider(
modelType,
extension,
viewType,
options,
capabilities,
supportsMultipleEditorsPerDocument,
serializeBuffersForPostMessage
) {
if (( this._editorProviders.has(viewType))) {
throw ( new Error(`Provider for ${viewType} already registered`));
}
const disposables = ( new DisposableStore());
disposables.add(this._customEditorService.registerCustomEditorCapabilities(viewType, {
supportsMultipleEditorsPerDocument,
isTextEditor: modelType === CustomEditorModelType.Text,
supportsInlineDiff: capabilities.supportsInlineDiff,
supportsSideBySideDiff: capabilities.supportsSideBySideDiff
}));
disposables.add(this._webviewWorkbenchService.registerResolver({
canResolve: webviewInput => {
return (webviewInput instanceof CustomEditorInput || webviewInput instanceof CustomEditorDiffInput || webviewInput instanceof CustomEditorSideBySideDiffInput) && webviewInput.viewType === viewType;
},
resolveWebview: async (webviewInput, cancellation) => {
if (!(webviewInput instanceof CustomEditorInput || webviewInput instanceof CustomEditorDiffInput || webviewInput instanceof CustomEditorSideBySideDiffInput)) {
return;
}
const handle = generateUuid();
webviewInput.webview.origin = this._webviewOriginStore.getOrigin(viewType, extension.id);
this.mainThreadWebviewPanels.addWebviewInput(handle, webviewInput, {
serializeBuffersForPostMessage
});
webviewInput.webview.options = options;
webviewInput.webview.extension = extension;
const resource = webviewInput instanceof CustomEditorDiffInput ? webviewInput.modifiedResource : webviewInput.resource;
let backupId;
if (webviewInput instanceof CustomEditorInput) {
backupId = webviewInput.backupId;
if (webviewInput.oldResource && !webviewInput.backupId) {
const backup = this._editorRenameBackups.get(( webviewInput.oldResource.toString()));
backupId = backup?.backupId;
this._editorRenameBackups.delete(( webviewInput.oldResource.toString()));
}
}
let modelRef;
const additionalModelRefs = ( new DisposableStore());
try {
modelRef = await this.getOrCreateCustomEditorModel(modelType, resource, viewType, {
backupId
}, cancellation);
if (webviewInput instanceof CustomEditorDiffInput && !isEqual(webviewInput.originalResource, resource)) {
additionalModelRefs.add(
await this.getOrCreateCustomEditorModel(modelType, webviewInput.originalResource, viewType, {}, cancellation)
);
} else if (modelType === CustomEditorModelType.Text && webviewInput instanceof CustomEditorSideBySideDiffInput) {
const otherResource = webviewInput.side === "original" ? webviewInput.modifiedResource : webviewInput.originalResource;
if (!isEqual(otherResource, resource)) {
additionalModelRefs.add(
await this.getOrCreateCustomEditorModel(modelType, otherResource, viewType, {}, cancellation)
);
}
}
} catch (error) {
onUnexpectedError(error);
webviewInput.webview.setHtml(this.mainThreadWebview.getWebviewResolvedFailedContent(viewType));
additionalModelRefs.dispose();
modelRef?.dispose();
return;
}
if (!modelRef) {
additionalModelRefs.dispose();
return;
}
let resolvedModelRef = modelRef;
if (cancellation.isCancellationRequested) {
additionalModelRefs.dispose();
resolvedModelRef.dispose();
return;
}
const disposeModelRefs = () => {
additionalModelRefs.dispose();
if (resolvedModelRef.object.isDirty()) {
const sub = resolvedModelRef.object.onDidChangeDirty(() => {
if (!resolvedModelRef.object.isDirty()) {
sub.dispose();
resolvedModelRef.dispose();
}
});
return;
}
resolvedModelRef.dispose();
};
const disposeSub = webviewInput.webview.onDidDispose(() => {
disposeSub.dispose();
inputDisposeSub.dispose();
disposeModelRefs();
});
const inputDisposeSub = webviewInput.onWillDispose(() => {
inputDisposeSub.dispose();
disposeSub.dispose();
disposeModelRefs();
});
if (webviewInput instanceof CustomEditorInput && capabilities.supportsMove) {
webviewInput.onMove(async newResource => {
const oldModel = resolvedModelRef;
resolvedModelRef = await this.getOrCreateCustomEditorModel(modelType, newResource, viewType, {}, CancellationToken.None);
this._proxyCustomEditors.$onMoveCustomEditor(handle, newResource, viewType);
oldModel.dispose();
});
}
try {
const initData = {
title: webviewInput.getTitle(),
contentOptions: webviewInput.webview.contentOptions,
options: webviewInput.webview.options,
active: webviewInput === this._editorService.activeEditor
};
const position = editorGroupToColumn(this._editorGroupService, webviewInput.group || 0);
if (webviewInput instanceof CustomEditorDiffInput) {
const originalResource = modelType === CustomEditorModelType.Text ? this._uriIdentityService.asCanonicalUri(webviewInput.originalResource) : webviewInput.originalResource;
const modifiedResource = modelType === CustomEditorModelType.Text ? this._uriIdentityService.asCanonicalUri(webviewInput.modifiedResource) : webviewInput.modifiedResource;
await this._proxyCustomEditors.$resolveCustomEditorInlineDiff(
originalResource,
modifiedResource,
handle,
viewType,
initData,
position,
cancellation
);
} else if (webviewInput instanceof CustomEditorSideBySideDiffInput) {
await this.resolveCustomEditorSideBySideDiff(
modelType,
webviewInput,
handle,
viewType,
initData,
position,
cancellation
);
} else {
const actualResource = modelType === CustomEditorModelType.Text ? this._uriIdentityService.asCanonicalUri(resource) : resource;
await this._proxyCustomEditors.$resolveCustomEditor(actualResource, handle, viewType, initData, position, cancellation);
}
} catch (error) {
onUnexpectedError(error);
webviewInput.webview.setHtml(this.mainThreadWebview.getWebviewResolvedFailedContent(viewType));
additionalModelRefs.dispose();
resolvedModelRef.dispose();
return;
}
}
}));
this._editorProviders.set(viewType, disposables);
}
resolveCustomEditorSideBySideDiff(
modelType,
webviewInput,
handle,
viewType,
initData,
position,
cancellation
) {
let pending = this._pendingSideBySideDiffResolutions.get(webviewInput.diffId);
if (!pending) {
pending = {
promise: ( new DeferredPromise()),
cancellation: ( new CancellationTokenSource()),
disposables: ( new DisposableStore())
};
this._pendingSideBySideDiffResolutions.set(webviewInput.diffId, pending);
}
const cleanup = () => {
this._pendingSideBySideDiffResolutions.delete(webviewInput.diffId);
pending.disposables.dispose();
pending.cancellation.dispose();
};
pending.disposables.add(cancellation.onCancellationRequested(() => {
pending.cancellation.cancel();
if (!pending.started) {
pending.promise.cancel();
cleanup();
}
}));
pending[webviewInput.side] = {
handle,
initData
};
if (pending.original && pending.modified && !pending.started) {
pending.started = true;
pending.promise.settleWith((async () => {
try {
const originalResource = modelType === CustomEditorModelType.Text ? this._uriIdentityService.asCanonicalUri(webviewInput.originalResource) : webviewInput.originalResource;
const modifiedResource = modelType === CustomEditorModelType.Text ? this._uriIdentityService.asCanonicalUri(webviewInput.modifiedResource) : webviewInput.modifiedResource;
await this._proxyCustomEditors.$resolveCustomEditorSideBySideDiff(originalResource, modifiedResource, {
original: pending.original.handle,
modified: pending.modified.handle
}, viewType, {
original: pending.original.initData,
modified: pending.modified.initData
}, position, pending.cancellation.token);
} finally {
cleanup();
}
})());
}
return pending.promise.p;
}
$unregisterEditorProvider(viewType) {
if (!( this._editorProviders.has(viewType))) {
throw ( new Error(`No provider for ${viewType} registered`));
}
this._editorProviders.deleteAndDispose(viewType);
this._customEditorService.models.disposeAllModelsForView(viewType);
}
async getOrCreateCustomEditorModel(modelType, resource, viewType, options, cancellation) {
const existingModel = this._customEditorService.models.tryRetain(resource, viewType);
if (existingModel) {
return existingModel;
}
switch (modelType) {
case CustomEditorModelType.Text:
{
const model = CustomTextEditorModel.create(this._instantiationService, viewType, resource);
return this._customEditorService.models.add(resource, viewType, model);
}
case CustomEditorModelType.Custom:
{
const model = MainThreadCustomEditorModel.create(
this._instantiationService,
this._proxyCustomEditors,
viewType,
resource,
options,
this._untitledTextEditorService,
() => {
return Array.from(this.mainThreadWebviewPanels.webviewInputs).filter(
editor => (editor instanceof CustomEditorInput && isEqual(editor.resource, resource)) || (editor instanceof CustomEditorDiffInput && (isEqual(editor.originalResource, resource) || isEqual(editor.modifiedResource, resource))) || (editor instanceof CustomEditorSideBySideDiffInput && isEqual(editor.resource, resource))
);
},
cancellation
);
return this._customEditorService.models.add(resource, viewType, model);
}
}
}
async $onDidEdit(resourceComponents, viewType, editId, label) {
const model = await this.getCustomEditorModel(resourceComponents, viewType);
model.pushEdit(editId, label);
}
async $onContentChange(resourceComponents, viewType) {
const model = await this.getCustomEditorModel(resourceComponents, viewType);
model.changeContent();
}
async getCustomEditorModel(resourceComponents, viewType) {
const resource = URI.revive(resourceComponents);
const model = await this._customEditorService.models.get(resource, viewType);
if (!model || !(model instanceof MainThreadCustomEditorModel)) {
throw ( new Error("Could not find model for webview editor"));
}
return model;
}
async onWillRunWorkingCopyFileOperation(e) {
if (e.operation !== FileOperation.MOVE) {
return;
}
e.waitUntil((async () => {
const models = [];
for (const file of e.files) {
if (file.source) {
models.push(...(await this._customEditorService.models.getAllModels(file.source)));
}
}
for (const model of models) {
if (model instanceof MainThreadCustomEditorModel && model.isDirty()) {
const workingCopy = await model.backup(CancellationToken.None);
if (workingCopy.meta) {
this._editorRenameBackups.set(( model.editorResource.toString()), workingCopy.meta);
}
}
}
})());
}
};
MainThreadCustomEditors = ( __decorate([( __param(3, IExtensionService)), ( __param(4, IStorageService)), ( __param(5, IWorkingCopyService)), ( __param(6, IWorkingCopyFileService)), ( __param(7, ICustomEditorService)), ( __param(8, IEditorGroupsService)), ( __param(9, IEditorService)), ( __param(10, IInstantiationService)), ( __param(11, IWebviewWorkbenchService)), ( __param(12, IUriIdentityService)), ( __param(13, IUntitledTextEditorService))], MainThreadCustomEditors));
var HotExitState;
(function(HotExitState) {
let Type;
(function(Type) {
Type[Type["Allowed"] = 0] = "Allowed";
Type[Type["NotAllowed"] = 1] = "NotAllowed";
Type[Type["Pending"] = 2] = "Pending";
})(Type = HotExitState.Type || (HotExitState.Type = {}));
HotExitState.Allowed = ( Object.freeze({
type: Type.Allowed
}));
HotExitState.NotAllowed = ( Object.freeze({
type: Type.NotAllowed
}));
class Pending {
constructor(operation) {
this.operation = operation;
this.type = Type.Pending;
}
}
HotExitState.Pending = Pending;
})(HotExitState || (HotExitState = {}));
let MainThreadCustomEditorModel = MainThreadCustomEditorModel_1 = class MainThreadCustomEditorModel extends ResourceWorkingCopy {
static async create(
instantiationService,
proxy,
viewType,
resource,
options,
untitledTextEditorService,
getEditors,
cancellation
) {
const editors = getEditors();
let untitledDocumentData;
const primaryCustomEditorInput = editors.find(editor => editor instanceof CustomEditorInput);
if (primaryCustomEditorInput) {
untitledDocumentData = primaryCustomEditorInput.untitledDocumentData;
}
const {
editable
} = await proxy.$createCustomDocument(resource, viewType, options.backupId, untitledDocumentData, cancellation);
if (untitledDocumentData && resource.scheme === Schemas.untitled) {
untitledTextEditorService.get(resource)?.revert();
}
return instantiationService.createInstance(
MainThreadCustomEditorModel_1,
proxy,
viewType,
resource,
!!options.backupId,
editable,
!!untitledDocumentData,
getEditors
);
}
constructor(
_proxy,
_viewType,
_editorResource,
fromBackup,
_editable,
startDirty,
_getEditors,
_fileDialogService,
fileService,
_labelService,
_undoService,
_environmentService,
workingCopyService,
_pathService,
extensionService
) {
super(
MainThreadCustomEditorModel_1.toWorkingCopyResource(_viewType, _editorResource),
fileService
);
this._proxy = _proxy;
this._viewType = _viewType;
this._editorResource = _editorResource;
this._editable = _editable;
this._getEditors = _getEditors;
this._fileDialogService = _fileDialogService;
this._labelService = _labelService;
this._undoService = _undoService;
this._environmentService = _environmentService;
this._pathService = _pathService;
this._fromBackup = false;
this._hotExitState = HotExitState.Allowed;
this._currentEditIndex = -1;
this._savePoint = -1;
this._edits = [];
this.typeId = NO_TYPE_ID;
this._onDidChangeDirty = this._register(( new Emitter()));
this.onDidChangeDirty = this._onDidChangeDirty.event;
this._onDidChangeContent = this._register(( new Emitter()));
this.onDidChangeContent = this._onDidChangeContent.event;
this._onDidSave = this._register(( new Emitter()));
this.onDidSave = this._onDidSave.event;
this.onDidChangeReadonly = Event.None;
this._fromBackup = fromBackup;
this._isDirtyFromContentChange = startDirty;
if (_editable) {
this._register(workingCopyService.registerWorkingCopy(this));
this._register(extensionService.onWillStop(e => {
e.veto(true, ( localize(
2591,
"An extension provided editor for '{0}' is still open that would close otherwise.",
this.name
)));
}));
}
}
get editorResource() {
return this._editorResource;
}
dispose() {
if (this._editable) {
this._undoService.removeElements(this._editorResource);
}
this._proxy.$disposeCustomDocument(this._editorResource, this._viewType);
super.dispose();
}
static toWorkingCopyResource(viewType, resource) {
const authority = viewType.replace(/[^a-z0-9\-_]/gi, "-");
const path = `/${multibyteAwareBtoa(( resource.with({
query: null,
fragment: null
}).toString(true)))}`;
return ( URI.from({
scheme: Schemas.vscodeCustomEditor,
authority: authority,
path: path,
query: JSON.stringify(resource.toJSON())
}));
}
get name() {
return basename(this._labelService.getUriLabel(this._editorResource));
}
get capabilities() {
return this.isUntitled() ? WorkingCopyCapabilities.Untitled : WorkingCopyCapabilities.None;
}
isDirty() {
if (this._isDirtyFromContentChange) {
return true;
}
if (this._edits.length > 0) {
return this._savePoint !== this._currentEditIndex;
}
return this._fromBackup;
}
isUntitled() {
return this._editorResource.scheme === Schemas.untitled;
}
isReadonly() {
return !this._editable;
}
get viewType() {
return this._viewType;
}
get backupId() {
return this._backupId;
}
pushEdit(editId, label) {
if (!this._editable) {
throw ( new Error("Document is not editable"));
}
this.change(() => {
this.spliceEdits(editId);
this._currentEditIndex = this._edits.length - 1;
});
this._undoService.pushElement({
type: UndoRedoElementType.Resource,
resource: this._editorResource,
label: label ?? ( localize(2592, "Edit")),
code: "undoredo.customEditorEdit",
undo: () => this.undo(),
redo: () => this.redo()
});
}
changeContent() {
this.change(() => {
this._isDirtyFromContentChange = true;
});
}
async undo() {
if (!this._editable) {
return;
}
if (this._currentEditIndex < 0) {
return;
}
const undoneEdit = this._edits[this._currentEditIndex];
this.change(() => {
--this._currentEditIndex;
});
await this._proxy.$undo(this._editorResource, this.viewType, undoneEdit, this.isDirty());
}
async redo() {
if (!this._editable) {
return;
}
if (this._currentEditIndex >= this._edits.length - 1) {
return;
}
const redoneEdit = this._edits[this._currentEditIndex + 1];
this.change(() => {
++this._currentEditIndex;
});
await this._proxy.$redo(this._editorResource, this.viewType, redoneEdit, this.isDirty());
}
spliceEdits(editToInsert) {
const start = this._currentEditIndex + 1;
const toRemove = this._edits.length - this._currentEditIndex;
const removedEdits = typeof editToInsert === "number" ? this._edits.splice(start, toRemove, editToInsert) : this._edits.splice(start, toRemove);
if (removedEdits.length) {
this._proxy.$disposeEdits(this._editorResource, this._viewType, removedEdits);
}
}
change(makeEdit) {
const wasDirty = this.isDirty();
makeEdit();
this._onDidChangeContent.fire();
if (this.isDirty() !== wasDirty) {
this._onDidChangeDirty.fire();
}
}
async revert(options) {
if (!this._editable) {
return;
}
if (this._currentEditIndex === this._savePoint && !this._isDirtyFromContentChange && !this._fromBackup) {
return;
}
if (!options?.soft) {
this._proxy.$revert(this._editorResource, this.viewType, CancellationToken.None);
}
this.change(() => {
this._isDirtyFromContentChange = false;
this._fromBackup = false;
this._currentEditIndex = this._savePoint;
this.spliceEdits();
});
}
async save(options) {
const result = !!(await this.saveCustomEditor(options));
if (result) {
this._onDidSave.fire({
reason: options?.reason,
source: options?.source
});
}
return result;
}
async saveCustomEditor(options) {
if (!this._editable) {
return undefined;
}
if (this.isUntitled()) {
const targetUri = await this.suggestUntitledSavePath(options);
if (!targetUri) {
return undefined;
}
await this.saveCustomEditorAs(this._editorResource, targetUri, options);
return targetUri;
}
const savePromise = createCancelablePromise(token => this._proxy.$onSave(this._editorResource, this.viewType, token));
this._ongoingSave?.cancel();
this._ongoingSave = savePromise;
try {
await savePromise;
if (this._ongoingSave === savePromise) {
this.change(() => {
this._isDirtyFromContentChange = false;
this._savePoint = this._currentEditIndex;
this._fromBackup = false;
});
}
} finally {
if (this._ongoingSave === savePromise) {
this._ongoingSave = undefined;
}
}
return this._editorResource;
}
suggestUntitledSavePath(options) {
if (!this.isUntitled()) {
throw ( new Error("Resource is not untitled"));
}
const remoteAuthority = this._environmentService.remoteAuthority;
const localResource = toLocalResource(this._editorResource, remoteAuthority, this._pathService.defaultUriScheme);
return this._fileDialogService.pickFileToSave(localResource, options?.availableFileSystems);
}
async saveCustomEditorAs(resource, targetResource, _options) {
if (this._editable) {
await createCancelablePromise(
token => this._proxy.$onSaveAs(this._editorResource, this.viewType, targetResource, token)
);
this.change(() => {
this._isDirtyFromContentChange = false;
this._savePoint = this._currentEditIndex;
this._fromBackup = false;
});
return true;
} else {
await this.fileService.copy(resource, targetResource, false );
return true;
}
}
get canHotExit() {
return typeof this._backupId === "string" && this._hotExitState.type === HotExitState.Type.Allowed;
}
async backup(token) {
const editors = this._getEditors();
if (!editors.length) {
throw ( new Error("No editors found for resource, cannot back up"));
}
const primaryEditor = editors[0];
const backupMeta = {
viewType: this.viewType,
editorResource: this._editorResource,
customTitle: primaryEditor.getWebviewTitle(),
iconPath: primaryEditor.iconPath,
backupId: "",
extension: primaryEditor.extension ? {
id: primaryEditor.extension.id.value,
location: primaryEditor.extension.location
} : undefined,
webview: {
origin: primaryEditor.webview.origin,
options: primaryEditor.webview.options,
state: primaryEditor.webview.state
}
};
const backupData = {
meta: backupMeta
};
if (!this._editable) {
return backupData;
}
if (this._hotExitState.type === HotExitState.Type.Pending) {
this._hotExitState.operation.cancel();
}
const pendingState = new HotExitState.Pending(createCancelablePromise(
token => this._proxy.$backup(this._editorResource.toJSON(), this.viewType, token)
));
this._hotExitState = pendingState;
token.onCancellationRequested(() => {
pendingState.operation.cancel();
});
let errorMessage = "";
try {
const backupId = await pendingState.operation;
if (this._hotExitState === pendingState) {
this._hotExitState = HotExitState.Allowed;
backupData.meta.backupId = backupId;
this._backupId = backupId;
}
} catch (e) {
if (isCancellationError(e)) {
throw e;
}
if (this._hotExitState === pendingState) {
this._hotExitState = HotExitState.NotAllowed;
}
if (e.message) {
errorMessage = e.message;
}
}
if (this._hotExitState === HotExitState.Allowed) {
return backupData;
}
throw ( new Error(`Cannot backup in this state: ${errorMessage}`));
}
};
MainThreadCustomEditorModel = MainThreadCustomEditorModel_1 = ( __decorate([( __param(7, IFileDialogService)), ( __param(8, IFileService)), ( __param(9, ILabelService)), ( __param(10, IUndoRedoService)), ( __param(11, IWorkbenchEnvironmentService)), ( __param(12, IWorkingCopyService)), ( __param(13, IPathService)), ( __param(14, IExtensionService))], MainThreadCustomEditorModel));
export { MainThreadCustomEditors };