mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-25 09:35:37 -05:00
Merge from vscode 2e5312cd61ff99c570299ecc122c52584265eda2
This commit is contained in:
committed by
Anthony Dresser
parent
3603f55d97
commit
7f1d8fc32f
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user