Merge from vscode 2e5312cd61ff99c570299ecc122c52584265eda2

This commit is contained in:
ADS Merger
2020-04-23 02:50:35 +00:00
committed by Anthony Dresser
parent 3603f55d97
commit 7f1d8fc32f
659 changed files with 22709 additions and 12497 deletions

View File

@@ -133,6 +133,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@IWebviewWorkbenchService private readonly _webviewWorkbenchService: IWebviewWorkbenchService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IBackupFileService private readonly _backupService: IBackupFileService,
) {
super();
@@ -151,7 +152,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
this.updateWebviewViewStates(this._editorService.activeEditor);
}));
// This reviver's only job is to activate webview panel extensions
// This reviver's only job is to activate extensions.
// This should trigger the real reviver to be registered from the extension host side.
this._register(_webviewWorkbenchService.registerResolver({
canResolve: (webview: WebviewInput) => {
@@ -305,11 +306,11 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
}
public $registerTextEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, capabilities: extHostProtocol.CustomTextEditorCapabilities): void {
this.registerEditorProvider(ModelType.Text, extensionData, viewType, options, capabilities);
this.registerEditorProvider(ModelType.Text, extensionData, viewType, options, capabilities, true);
}
public $registerCustomEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions): void {
this.registerEditorProvider(ModelType.Custom, extensionData, viewType, options, {});
public $registerCustomEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, supportsMultipleEditorsPerResource: boolean): void {
this.registerEditorProvider(ModelType.Custom, extensionData, viewType, options, {}, supportsMultipleEditorsPerResource);
}
private registerEditorProvider(
@@ -318,11 +319,16 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
viewType: string,
options: modes.IWebviewPanelOptions,
capabilities: extHostProtocol.CustomTextEditorCapabilities,
supportsMultipleEditorsPerResource: boolean,
): DisposableStore {
if (this._editorProviders.has(viewType)) {
throw new Error(`Provider for ${viewType} already registered`);
}
this._customEditorService.registerCustomEditorCapabilities(viewType, {
supportsMultipleEditorsPerResource
});
const extension = reviveWebviewExtension(extensionData);
const disposables = new DisposableStore();
@@ -341,7 +347,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
let modelRef: IReference<ICustomEditorModel>;
try {
modelRef = await this.getOrCreateCustomEditorModel(modelType, resource, viewType, cancellation);
modelRef = await this.getOrCreateCustomEditorModel(modelType, resource, viewType, { backupId: webviewInput.backupId }, cancellation);
} catch (error) {
onUnexpectedError(error);
webviewInput.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(viewType);
@@ -360,7 +366,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
if (capabilities.supportsMove) {
webviewInput.onMove(async (newResource: URI) => {
const oldModel = modelRef;
modelRef = await this.getOrCreateCustomEditorModel(modelType, newResource, viewType, CancellationToken.None);
modelRef = await this.getOrCreateCustomEditorModel(modelType, newResource, viewType, {}, CancellationToken.None);
this._proxy.$onMoveCustomEditor(handle, newResource, viewType);
oldModel.dispose();
});
@@ -398,6 +404,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
modelType: ModelType,
resource: URI,
viewType: string,
options: { backupId?: string },
cancellation: CancellationToken,
): Promise<IReference<ICustomEditorModel>> {
const existingModel = this._customEditorService.models.tryRetain(resource, viewType);
@@ -405,26 +412,33 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
return existingModel;
}
const model = modelType === ModelType.Text
? CustomTextEditorModel.create(this._instantiationService, viewType, resource)
: MainThreadCustomEditorModel.create(this._instantiationService, this._proxy, viewType, resource, () => {
return Array.from(this._webviewInputs)
.filter(editor => editor instanceof CustomEditorInput && isEqual(editor.resource, resource)) as CustomEditorInput[];
}, cancellation);
return this._customEditorService.models.add(resource, viewType, model);
switch (modelType) {
case ModelType.Text:
{
const model = CustomTextEditorModel.create(this._instantiationService, viewType, resource);
return this._customEditorService.models.add(resource, viewType, model);
}
case ModelType.Custom:
{
const model = MainThreadCustomEditorModel.create(this._instantiationService, this._proxy, viewType, resource, options, () => {
return Array.from(this._webviewInputs)
.filter(editor => editor instanceof CustomEditorInput && isEqual(editor.resource, resource)) as CustomEditorInput[];
}, cancellation, this._backupService);
return this._customEditorService.models.add(resource, viewType, model);
}
}
}
public async $onDidEdit(resourceComponents: UriComponents, viewType: string, editId: number, label: string | undefined): Promise<void> {
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');
}
const model = await this.getCustomEditorModel(resourceComponents, viewType);
model.pushEdit(editId, label);
}
public async $onContentChange(resourceComponents: UriComponents, viewType: string): Promise<void> {
const model = await this.getCustomEditorModel(resourceComponents, viewType);
model.changeContent();
}
private hookupWebviewEventDelegate(handle: extHostProtocol.WebviewPanelHandle, input: WebviewInput) {
const disposables = new DisposableStore();
@@ -531,6 +545,15 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
return this._webviewInputs.getInputForHandle(handle);
}
private async getCustomEditorModel(resourceComponents: UriComponents, viewType: string) {
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;
}
private static getWebviewResolvedFailedContent(viewType: string) {
return `<!DOCTYPE html>
<html>
@@ -577,7 +600,7 @@ namespace HotExitState {
readonly type = Type.Pending;
constructor(
public readonly operation: CancelablePromise<void>,
public readonly operation: CancelablePromise<string>,
) { }
}
@@ -588,58 +611,58 @@ namespace HotExitState {
class MainThreadCustomEditorModel extends Disposable implements ICustomEditorModel, IWorkingCopy {
private _hotExitState: HotExitState.State = HotExitState.Allowed;
private readonly _fromBackup: boolean = false;
private _currentEditIndex: number = -1;
private _savePoint: number = -1;
private readonly _edits: Array<number> = [];
private _fromBackup: boolean = false;
private _isDirtyFromContentChange = false;
private _ongoingSave?: CancelablePromise<void>;
public static async create(
instantiationService: IInstantiationService,
proxy: extHostProtocol.ExtHostWebviewsShape,
viewType: string,
resource: URI,
options: { backupId?: string },
getEditors: () => CustomEditorInput[],
cancellation: CancellationToken,
_backupFileService: IBackupFileService,
) {
const { editable } = await proxy.$createWebviewCustomEditorDocument(resource, viewType, cancellation);
const model = instantiationService.createInstance(MainThreadCustomEditorModel, proxy, viewType, resource, editable, getEditors);
await model.init();
return model;
const { editable } = await proxy.$createCustomDocument(resource, viewType, options.backupId, cancellation);
return instantiationService.createInstance(MainThreadCustomEditorModel, proxy, viewType, resource, !!options.backupId, editable, getEditors);
}
constructor(
private readonly _proxy: extHostProtocol.ExtHostWebviewsShape,
private readonly _viewType: string,
private readonly _editorResource: URI,
fromBackup: boolean,
private readonly _editable: boolean,
private readonly _getEditors: () => CustomEditorInput[],
@IWorkingCopyService workingCopyService: IWorkingCopyService,
@ILabelService private readonly _labelService: ILabelService,
@IFileService private readonly _fileService: IFileService,
@IUndoRedoService private readonly _undoService: IUndoRedoService,
@IBackupFileService private readonly _backupFileService: IBackupFileService,
) {
super();
if (_editable) {
this._register(workingCopyService.registerWorkingCopy(this));
}
this._fromBackup = fromBackup;
}
get editorResource() {
return this._editorResource;
}
async init(): Promise<void> {
const backup = await this._backupFileService.resolve<CustomDocumentBackupData>(this.resource);
this._fromBackup = !!backup;
}
dispose() {
if (this._editable) {
this._undoService.removeElements(this._editorResource);
}
this._proxy.$disposeWebviewCustomEditorDocument(this._editorResource, this._viewType);
this._proxy.$disposeCustomDocument(this._editorResource, this._viewType);
super.dispose();
}
@@ -647,11 +670,15 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
public get resource() {
// Make sure each custom editor has a unique resource for backup and edits
return MainThreadCustomEditorModel.toWorkingCopyResource(this._viewType, this._editorResource);
}
private static toWorkingCopyResource(viewType: string, resource: URI) {
return URI.from({
scheme: Schemas.vscodeCustomEditor,
authority: this._viewType,
path: this._editorResource.path,
query: JSON.stringify(this._editorResource.toJSON()),
authority: viewType,
path: resource.path,
query: JSON.stringify(resource.toJSON()),
});
}
@@ -664,6 +691,9 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
}
public isDirty(): boolean {
if (this._isDirtyFromContentChange) {
return true;
}
if (this._edits.length > 0) {
return this._savePoint !== this._currentEditIndex;
}
@@ -705,6 +735,12 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
});
}
public changeContent() {
this.change(() => {
this._isDirtyFromContentChange = true;
});
}
private async undo(): Promise<void> {
if (!this._editable) {
return;
@@ -719,15 +755,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
this.change(() => {
--this._currentEditIndex;
});
await this._proxy.$undo(this._editorResource, this.viewType, undoneEdit, this.getEditState());
}
private getEditState(): extHostProtocol.CustomDocumentEditState {
return {
allEdits: this._edits,
currentIndex: this._currentEditIndex,
saveIndex: this._savePoint,
};
await this._proxy.$undo(this._editorResource, this.viewType, undoneEdit, this.isDirty());
}
private async redo(): Promise<void> {
@@ -744,7 +772,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
this.change(() => {
++this._currentEditIndex;
});
await this._proxy.$redo(this._editorResource, this.viewType, redoneEdit, this.getEditState());
await this._proxy.$redo(this._editorResource, this.viewType, redoneEdit, this.isDirty());
}
private spliceEdits(editToInsert?: number) {
@@ -775,21 +803,13 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
return;
}
if (this._currentEditIndex === this._savePoint) {
if (this._currentEditIndex === this._savePoint && !this._isDirtyFromContentChange) {
return;
}
let editsToUndo: number[] = [];
let editsToRedo: number[] = [];
if (this._currentEditIndex >= this._savePoint) {
editsToUndo = this._edits.slice(this._savePoint, this._currentEditIndex).reverse();
} else if (this._currentEditIndex < this._savePoint) {
editsToRedo = this._edits.slice(this._currentEditIndex, this._savePoint);
}
this._proxy.$revert(this._editorResource, this.viewType, { undoneEdits: editsToUndo, redoneEdits: editsToRedo }, this.getEditState());
this._proxy.$revert(this._editorResource, this.viewType, CancellationToken.None);
this.change(() => {
this._isDirtyFromContentChange = false;
this._currentEditIndex = this._savePoint;
this.spliceEdits();
});
@@ -804,11 +824,23 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
return undefined;
}
// TODO: handle save untitled case
// TODO: handle cancellation
await createCancelablePromise(token => this._proxy.$onSave(this._editorResource, this.viewType, token));
const savePromise = createCancelablePromise(token => this._proxy.$onSave(this._editorResource, this.viewType, token));
this._ongoingSave?.cancel();
this._ongoingSave = savePromise;
this.change(() => {
this._isDirtyFromContentChange = false;
this._savePoint = this._currentEditIndex;
});
try {
await savePromise;
} finally {
if (this._ongoingSave === savePromise) {
this._ongoingSave = undefined;
}
}
return this._editorResource;
}
@@ -838,6 +870,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
meta: {
viewType: this.viewType,
editorResource: this._editorResource,
backupId: '',
extension: primaryEditor.extension ? {
id: primaryEditor.extension.id.value,
location: primaryEditor.extension.location,
@@ -864,10 +897,11 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
this._hotExitState = pendingState;
try {
await pendingState.operation;
const backupId = await pendingState.operation;
// Make sure state has not changed in the meantime
if (this._hotExitState === pendingState) {
this._hotExitState = HotExitState.Allowed;
backupData.meta!.backupId = backupId;
}
} catch (e) {
// Make sure state has not changed in the meantime