mirror of
https://github.com/Microsoft/vscode
synced 2024-09-18 01:58:27 +00:00
Move custom editors into own ext host services
This commit is contained in:
parent
4fd7f660a4
commit
6db81f6ab2
|
@ -118,7 +118,9 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
|||
]);
|
||||
|
||||
private readonly _proxy: extHostProtocol.ExtHostWebviewsShape;
|
||||
private readonly _viewsProxy: extHostProtocol.ExtHostWebviewViewsShape;
|
||||
private readonly _proxyViews: extHostProtocol.ExtHostWebviewViewsShape;
|
||||
private readonly _proxyCustomEditors: extHostProtocol.ExtHostCustomEditorsShape;
|
||||
|
||||
private readonly _webviewInputs = new WebviewInputStore();
|
||||
private readonly _revivers = new Map<string, IDisposable>();
|
||||
|
||||
|
@ -147,7 +149,8 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
|||
super();
|
||||
|
||||
this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviews);
|
||||
this._viewsProxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewViews);
|
||||
this._proxyViews = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewViews);
|
||||
this._proxyCustomEditors = context.getProxy(extHostProtocol.ExtHostContext.ExtHostCustomEditors);
|
||||
|
||||
this._register(_editorService.onDidActiveEditorChange(() => {
|
||||
const activeInput = this._editorService.activeEditor;
|
||||
|
@ -358,15 +361,15 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
|||
}
|
||||
|
||||
webviewView.onDidChangeVisibility(visible => {
|
||||
this._viewsProxy.$onDidChangeWebviewViewVisibility(handle, visible);
|
||||
this._proxyViews.$onDidChangeWebviewViewVisibility(handle, visible);
|
||||
});
|
||||
|
||||
webviewView.onDispose(() => {
|
||||
this._viewsProxy.$disposeWebviewView(handle);
|
||||
this._proxyViews.$disposeWebviewView(handle);
|
||||
});
|
||||
|
||||
try {
|
||||
await this._viewsProxy.$resolveWebviewView(handle, viewType, state, cancellation);
|
||||
await this._proxyViews.$resolveWebviewView(handle, viewType, state, cancellation);
|
||||
} catch (error) {
|
||||
onUnexpectedError(error);
|
||||
webviewView.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(viewType);
|
||||
|
@ -459,13 +462,13 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
|||
webviewInput.onMove(async (newResource: URI) => {
|
||||
const oldModel = modelRef;
|
||||
modelRef = await this.getOrCreateCustomEditorModel(modelType, newResource, viewType, {}, CancellationToken.None);
|
||||
this._proxy.$onMoveCustomEditor(handle, newResource, viewType);
|
||||
this._proxyCustomEditors.$onMoveCustomEditor(handle, newResource, viewType);
|
||||
oldModel.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
await this._proxy.$resolveWebviewEditor(resource, handle, viewType, webviewInput.getTitle(), editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), webviewInput.webview.options, cancellation);
|
||||
await this._proxyCustomEditors.$resolveWebviewEditor(resource, handle, viewType, webviewInput.getTitle(), editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), webviewInput.webview.options, cancellation);
|
||||
} catch (error) {
|
||||
onUnexpectedError(error);
|
||||
webviewInput.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(viewType);
|
||||
|
@ -510,7 +513,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
|||
}
|
||||
case ModelType.Custom:
|
||||
{
|
||||
const model = MainThreadCustomEditorModel.create(this._instantiationService, this._proxy, viewType, resource, options, () => {
|
||||
const model = MainThreadCustomEditorModel.create(this._instantiationService, this._proxyCustomEditors, viewType, resource, options, () => {
|
||||
return Array.from(this._webviewInputs)
|
||||
.filter(editor => editor instanceof CustomEditorInput && isEqual(editor.resource, resource)) as CustomEditorInput[];
|
||||
}, cancellation, this._backupService);
|
||||
|
@ -721,7 +724,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
|||
|
||||
public static async create(
|
||||
instantiationService: IInstantiationService,
|
||||
proxy: extHostProtocol.ExtHostWebviewsShape,
|
||||
proxy: extHostProtocol.ExtHostCustomEditorsShape,
|
||||
viewType: string,
|
||||
resource: URI,
|
||||
options: { backupId?: string },
|
||||
|
@ -734,7 +737,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
|||
}
|
||||
|
||||
constructor(
|
||||
private readonly _proxy: extHostProtocol.ExtHostWebviewsShape,
|
||||
private readonly _proxy: extHostProtocol.ExtHostCustomEditorsShape,
|
||||
private readonly _viewType: string,
|
||||
private readonly _editorResource: URI,
|
||||
fromBackup: boolean,
|
||||
|
|
|
@ -77,6 +77,7 @@ import { ExtHostNotebookConcatDocument } from 'vs/workbench/api/common/extHostNo
|
|||
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
|
||||
import { IExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer';
|
||||
import { ExtHostWebviewViews } from 'vs/workbench/api/common/extHostWebviewView';
|
||||
import { ExtHostCustomEditors } from 'vs/workbench/api/common/extHostCustomEditors';
|
||||
|
||||
export interface IExtensionApiFactory {
|
||||
(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode;
|
||||
|
@ -142,7 +143,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
|||
const extHostTheming = rpcProtocol.set(ExtHostContext.ExtHostTheming, new ExtHostTheming(rpcProtocol));
|
||||
const extHostAuthentication = rpcProtocol.set(ExtHostContext.ExtHostAuthentication, new ExtHostAuthentication(rpcProtocol));
|
||||
const extHostTimeline = rpcProtocol.set(ExtHostContext.ExtHostTimeline, new ExtHostTimeline(rpcProtocol, extHostCommands));
|
||||
const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol, initData.environment, extHostWorkspace, extHostLogService, extHostApiDeprecation, extHostDocuments, extensionStoragePaths));
|
||||
const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol, initData.environment, extHostWorkspace, extHostLogService, extHostApiDeprecation));
|
||||
const extHostCustomEditors = rpcProtocol.set(ExtHostContext.ExtHostCustomEditors, new ExtHostCustomEditors(rpcProtocol, extHostDocuments, extensionStoragePaths, extHostWebviews));
|
||||
const extHostWebviewViews = rpcProtocol.set(ExtHostContext.ExtHostWebviewViews, new ExtHostWebviewViews(rpcProtocol, extHostWebviews));
|
||||
|
||||
// Check that no named customers are missing
|
||||
|
@ -594,7 +596,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
|||
return extHostWebviews.registerWebviewPanelSerializer(extension, viewType, serializer);
|
||||
},
|
||||
registerCustomEditorProvider: (viewType: string, provider: vscode.CustomTextEditorProvider | vscode.CustomReadonlyEditorProvider, options: { webviewOptions?: vscode.WebviewPanelOptions, supportsMultipleEditorsPerDocument?: boolean } = {}) => {
|
||||
return extHostWebviews.registerCustomEditorProvider(extension, viewType, provider, options);
|
||||
return extHostCustomEditors.registerCustomEditorProvider(extension, viewType, provider, options);
|
||||
},
|
||||
registerDecorationProvider(provider: vscode.DecorationProvider) {
|
||||
checkProposedApiEnabled(extension);
|
||||
|
|
|
@ -651,7 +651,9 @@ export interface ExtHostWebviewsShape {
|
|||
$onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Promise<void>;
|
||||
|
||||
$deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise<void>;
|
||||
}
|
||||
|
||||
export interface ExtHostCustomEditorsShape {
|
||||
$resolveWebviewEditor(resource: UriComponents, newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, cancellation: CancellationToken): Promise<void>;
|
||||
$createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, cancellation: CancellationToken): Promise<{ editable: boolean }>;
|
||||
$disposeCustomDocument(resource: UriComponents, viewType: string): Promise<void>;
|
||||
|
@ -1749,6 +1751,7 @@ export const ExtHostContext = {
|
|||
ExtHostWorkspace: createExtId<ExtHostWorkspaceShape>('ExtHostWorkspace'),
|
||||
ExtHostWindow: createExtId<ExtHostWindowShape>('ExtHostWindow'),
|
||||
ExtHostWebviews: createExtId<ExtHostWebviewsShape>('ExtHostWebviews'),
|
||||
ExtHostCustomEditors: createExtId<ExtHostCustomEditorsShape>('ExtHostCustomEditors'),
|
||||
ExtHostWebviewViews: createExtId<ExtHostWebviewViewsShape>('ExtHostWebviewViews'),
|
||||
ExtHostEditorInsets: createExtId<ExtHostEditorInsetsShape>('ExtHostEditorInsets'),
|
||||
ExtHostProgress: createMainId<ExtHostProgressShape>('ExtHostProgress'),
|
||||
|
|
386
src/vs/workbench/api/common/extHostCustomEditors.ts
Normal file
386
src/vs/workbench/api/common/extHostCustomEditors.ts
Normal file
|
@ -0,0 +1,386 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { hash } from 'vs/base/common/hash';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { joinPath } from 'vs/base/common/resources';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
|
||||
import { ExtHostWebviews, toExtensionData } from 'vs/workbench/api/common/extHostWebview';
|
||||
import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor';
|
||||
import type * as vscode from 'vscode';
|
||||
import { Cache } from './cache';
|
||||
import * as extHostProtocol from './extHost.protocol';
|
||||
import * as extHostTypes from './extHostTypes';
|
||||
|
||||
|
||||
class CustomDocumentStoreEntry {
|
||||
|
||||
private _backupCounter = 1;
|
||||
|
||||
constructor(
|
||||
public readonly document: vscode.CustomDocument,
|
||||
private readonly _storagePath: URI | undefined,
|
||||
) { }
|
||||
|
||||
private readonly _edits = new Cache<vscode.CustomDocumentEditEvent>('custom documents');
|
||||
|
||||
private _backup?: vscode.CustomDocumentBackup;
|
||||
|
||||
addEdit(item: vscode.CustomDocumentEditEvent): number {
|
||||
return this._edits.add([item]);
|
||||
}
|
||||
|
||||
async undo(editId: number, isDirty: boolean): Promise<void> {
|
||||
await this.getEdit(editId).undo();
|
||||
if (!isDirty) {
|
||||
this.disposeBackup();
|
||||
}
|
||||
}
|
||||
|
||||
async redo(editId: number, isDirty: boolean): Promise<void> {
|
||||
await this.getEdit(editId).redo();
|
||||
if (!isDirty) {
|
||||
this.disposeBackup();
|
||||
}
|
||||
}
|
||||
|
||||
disposeEdits(editIds: number[]): void {
|
||||
for (const id of editIds) {
|
||||
this._edits.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
getNewBackupUri(): URI {
|
||||
if (!this._storagePath) {
|
||||
throw new Error('Backup requires a valid storage path');
|
||||
}
|
||||
const fileName = hashPath(this.document.uri) + (this._backupCounter++);
|
||||
return joinPath(this._storagePath, fileName);
|
||||
}
|
||||
|
||||
updateBackup(backup: vscode.CustomDocumentBackup): void {
|
||||
this._backup?.delete();
|
||||
this._backup = backup;
|
||||
}
|
||||
|
||||
disposeBackup(): void {
|
||||
this._backup?.delete();
|
||||
this._backup = undefined;
|
||||
}
|
||||
|
||||
private getEdit(editId: number): vscode.CustomDocumentEditEvent {
|
||||
const edit = this._edits.get(editId, 0);
|
||||
if (!edit) {
|
||||
throw new Error('No edit found');
|
||||
}
|
||||
return edit;
|
||||
}
|
||||
}
|
||||
|
||||
class CustomDocumentStore {
|
||||
private readonly _documents = new Map<string, CustomDocumentStoreEntry>();
|
||||
|
||||
public get(viewType: string, resource: vscode.Uri): CustomDocumentStoreEntry | undefined {
|
||||
return this._documents.get(this.key(viewType, resource));
|
||||
}
|
||||
|
||||
public add(viewType: string, document: vscode.CustomDocument, storagePath: URI | undefined): CustomDocumentStoreEntry {
|
||||
const key = this.key(viewType, document.uri);
|
||||
if (this._documents.has(key)) {
|
||||
throw new Error(`Document already exists for viewType:${viewType} resource:${document.uri}`);
|
||||
}
|
||||
const entry = new CustomDocumentStoreEntry(document, storagePath);
|
||||
this._documents.set(key, entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
public delete(viewType: string, document: vscode.CustomDocument) {
|
||||
const key = this.key(viewType, document.uri);
|
||||
this._documents.delete(key);
|
||||
}
|
||||
|
||||
private key(viewType: string, resource: vscode.Uri): string {
|
||||
return `${viewType}@@@${resource}`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const enum WebviewEditorType {
|
||||
Text,
|
||||
Custom
|
||||
}
|
||||
|
||||
type ProviderEntry = {
|
||||
readonly extension: IExtensionDescription;
|
||||
readonly type: WebviewEditorType.Text;
|
||||
readonly provider: vscode.CustomTextEditorProvider;
|
||||
} | {
|
||||
readonly extension: IExtensionDescription;
|
||||
readonly type: WebviewEditorType.Custom;
|
||||
readonly provider: vscode.CustomReadonlyEditorProvider;
|
||||
};
|
||||
|
||||
class EditorProviderStore {
|
||||
private readonly _providers = new Map<string, ProviderEntry>();
|
||||
|
||||
public addTextProvider(viewType: string, extension: IExtensionDescription, provider: vscode.CustomTextEditorProvider): vscode.Disposable {
|
||||
return this.add(WebviewEditorType.Text, viewType, extension, provider);
|
||||
}
|
||||
|
||||
public addCustomProvider(viewType: string, extension: IExtensionDescription, provider: vscode.CustomReadonlyEditorProvider): vscode.Disposable {
|
||||
return this.add(WebviewEditorType.Custom, viewType, extension, provider);
|
||||
}
|
||||
|
||||
public get(viewType: string): ProviderEntry | undefined {
|
||||
return this._providers.get(viewType);
|
||||
}
|
||||
|
||||
private add(type: WebviewEditorType, viewType: string, extension: IExtensionDescription, provider: vscode.CustomTextEditorProvider | vscode.CustomReadonlyEditorProvider): vscode.Disposable {
|
||||
if (this._providers.has(viewType)) {
|
||||
throw new Error(`Provider for viewType:${viewType} already registered`);
|
||||
}
|
||||
this._providers.set(viewType, { type, extension, provider } as ProviderEntry);
|
||||
return new extHostTypes.Disposable(() => this._providers.delete(viewType));
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditorsShape {
|
||||
|
||||
private readonly _proxy: extHostProtocol.MainThreadWebviewsShape;
|
||||
|
||||
private readonly _editorProviders = new EditorProviderStore();
|
||||
|
||||
private readonly _documents = new CustomDocumentStore();
|
||||
|
||||
constructor(
|
||||
mainContext: extHostProtocol.IMainContext,
|
||||
private readonly _extHostDocuments: ExtHostDocuments,
|
||||
private readonly _extensionStoragePaths: IExtensionStoragePaths | undefined,
|
||||
private readonly _extHostWebview: ExtHostWebviews,
|
||||
) {
|
||||
this._proxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadWebviews);
|
||||
}
|
||||
|
||||
public registerCustomEditorProvider(
|
||||
extension: IExtensionDescription,
|
||||
viewType: string,
|
||||
provider: vscode.CustomReadonlyEditorProvider | vscode.CustomTextEditorProvider,
|
||||
options: { webviewOptions?: vscode.WebviewPanelOptions, supportsMultipleEditorsPerDocument?: boolean },
|
||||
): vscode.Disposable {
|
||||
const disposables = new DisposableStore();
|
||||
if ('resolveCustomTextEditor' in provider) {
|
||||
disposables.add(this._editorProviders.addTextProvider(viewType, extension, provider));
|
||||
this._proxy.$registerTextEditorProvider(toExtensionData(extension), viewType, options.webviewOptions || {}, {
|
||||
supportsMove: !!provider.moveCustomTextEditor,
|
||||
});
|
||||
} else {
|
||||
disposables.add(this._editorProviders.addCustomProvider(viewType, extension, provider));
|
||||
|
||||
if (this.supportEditing(provider)) {
|
||||
disposables.add(provider.onDidChangeCustomDocument(e => {
|
||||
const entry = this.getCustomDocumentEntry(viewType, e.document.uri);
|
||||
if (isEditEvent(e)) {
|
||||
const editId = entry.addEdit(e);
|
||||
this._proxy.$onDidEdit(e.document.uri, viewType, editId, e.label);
|
||||
} else {
|
||||
this._proxy.$onContentChange(e.document.uri, viewType);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
this._proxy.$registerCustomEditorProvider(toExtensionData(extension), viewType, options.webviewOptions || {}, !!options.supportsMultipleEditorsPerDocument);
|
||||
}
|
||||
|
||||
return extHostTypes.Disposable.from(
|
||||
disposables,
|
||||
new extHostTypes.Disposable(() => {
|
||||
this._proxy.$unregisterEditorProvider(viewType);
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
async $createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, cancellation: CancellationToken) {
|
||||
const entry = this._editorProviders.get(viewType);
|
||||
if (!entry) {
|
||||
throw new Error(`No provider found for '${viewType}'`);
|
||||
}
|
||||
|
||||
if (entry.type !== WebviewEditorType.Custom) {
|
||||
throw new Error(`Invalid provide type for '${viewType}'`);
|
||||
}
|
||||
|
||||
const revivedResource = URI.revive(resource);
|
||||
const document = await entry.provider.openCustomDocument(revivedResource, { backupId }, cancellation);
|
||||
|
||||
let storageRoot: URI | undefined;
|
||||
if (this.supportEditing(entry.provider) && this._extensionStoragePaths) {
|
||||
storageRoot = this._extensionStoragePaths.workspaceValue(entry.extension) ?? this._extensionStoragePaths.globalValue(entry.extension);
|
||||
}
|
||||
this._documents.add(viewType, document, storageRoot);
|
||||
|
||||
return { editable: this.supportEditing(entry.provider) };
|
||||
}
|
||||
|
||||
async $disposeCustomDocument(resource: UriComponents, viewType: string): Promise<void> {
|
||||
const entry = this._editorProviders.get(viewType);
|
||||
if (!entry) {
|
||||
throw new Error(`No provider found for '${viewType}'`);
|
||||
}
|
||||
|
||||
if (entry.type !== WebviewEditorType.Custom) {
|
||||
throw new Error(`Invalid provider type for '${viewType}'`);
|
||||
}
|
||||
|
||||
const revivedResource = URI.revive(resource);
|
||||
const { document } = this.getCustomDocumentEntry(viewType, revivedResource);
|
||||
this._documents.delete(viewType, document);
|
||||
document.dispose();
|
||||
}
|
||||
|
||||
async $resolveWebviewEditor(
|
||||
resource: UriComponents,
|
||||
handle: extHostProtocol.WebviewPanelHandle,
|
||||
viewType: string,
|
||||
title: string,
|
||||
position: EditorViewColumn,
|
||||
options: modes.IWebviewOptions & modes.IWebviewPanelOptions,
|
||||
cancellation: CancellationToken,
|
||||
): Promise<void> {
|
||||
const entry = this._editorProviders.get(viewType);
|
||||
if (!entry) {
|
||||
throw new Error(`No provider found for '${viewType}'`);
|
||||
}
|
||||
|
||||
const webview = this._extHostWebview.createNewWebview(handle, options, entry.extension);
|
||||
const panel = this._extHostWebview.createNewWebviewPanel(handle, viewType, title, position, options, webview);
|
||||
|
||||
const revivedResource = URI.revive(resource);
|
||||
|
||||
switch (entry.type) {
|
||||
case WebviewEditorType.Custom:
|
||||
{
|
||||
const { document } = this.getCustomDocumentEntry(viewType, revivedResource);
|
||||
return entry.provider.resolveCustomEditor(document, panel, cancellation);
|
||||
}
|
||||
case WebviewEditorType.Text:
|
||||
{
|
||||
const document = this._extHostDocuments.getDocument(revivedResource);
|
||||
return entry.provider.resolveCustomTextEditor(document, panel, cancellation);
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new Error('Unknown webview provider type');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$disposeEdits(resourceComponents: UriComponents, viewType: string, editIds: number[]): void {
|
||||
const document = this.getCustomDocumentEntry(viewType, resourceComponents);
|
||||
document.disposeEdits(editIds);
|
||||
}
|
||||
|
||||
async $onMoveCustomEditor(handle: string, newResourceComponents: UriComponents, viewType: string): Promise<void> {
|
||||
const entry = this._editorProviders.get(viewType);
|
||||
if (!entry) {
|
||||
throw new Error(`No provider found for '${viewType}'`);
|
||||
}
|
||||
|
||||
if (!(entry.provider as vscode.CustomTextEditorProvider).moveCustomTextEditor) {
|
||||
throw new Error(`Provider does not implement move '${viewType}'`);
|
||||
}
|
||||
|
||||
const webview = this._extHostWebview.getWebviewPanel(handle);
|
||||
if (!webview) {
|
||||
throw new Error(`No webview found`);
|
||||
}
|
||||
|
||||
const resource = URI.revive(newResourceComponents);
|
||||
const document = this._extHostDocuments.getDocument(resource);
|
||||
await (entry.provider as vscode.CustomTextEditorProvider).moveCustomTextEditor!(document, webview, CancellationToken.None);
|
||||
}
|
||||
|
||||
async $undo(resourceComponents: UriComponents, viewType: string, editId: number, isDirty: boolean): Promise<void> {
|
||||
const entry = this.getCustomDocumentEntry(viewType, resourceComponents);
|
||||
return entry.undo(editId, isDirty);
|
||||
}
|
||||
|
||||
async $redo(resourceComponents: UriComponents, viewType: string, editId: number, isDirty: boolean): Promise<void> {
|
||||
const entry = this.getCustomDocumentEntry(viewType, resourceComponents);
|
||||
return entry.redo(editId, isDirty);
|
||||
}
|
||||
|
||||
async $revert(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void> {
|
||||
const entry = this.getCustomDocumentEntry(viewType, resourceComponents);
|
||||
const provider = this.getCustomEditorProvider(viewType);
|
||||
await provider.revertCustomDocument(entry.document, cancellation);
|
||||
entry.disposeBackup();
|
||||
}
|
||||
|
||||
async $onSave(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void> {
|
||||
const entry = this.getCustomDocumentEntry(viewType, resourceComponents);
|
||||
const provider = this.getCustomEditorProvider(viewType);
|
||||
await provider.saveCustomDocument(entry.document, cancellation);
|
||||
entry.disposeBackup();
|
||||
}
|
||||
|
||||
async $onSaveAs(resourceComponents: UriComponents, viewType: string, targetResource: UriComponents, cancellation: CancellationToken): Promise<void> {
|
||||
const entry = this.getCustomDocumentEntry(viewType, resourceComponents);
|
||||
const provider = this.getCustomEditorProvider(viewType);
|
||||
return provider.saveCustomDocumentAs(entry.document, URI.revive(targetResource), cancellation);
|
||||
}
|
||||
|
||||
async $backup(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise<string> {
|
||||
const entry = this.getCustomDocumentEntry(viewType, resourceComponents);
|
||||
const provider = this.getCustomEditorProvider(viewType);
|
||||
|
||||
const backup = await provider.backupCustomDocument(entry.document, {
|
||||
destination: entry.getNewBackupUri(),
|
||||
}, cancellation);
|
||||
entry.updateBackup(backup);
|
||||
return backup.id;
|
||||
}
|
||||
|
||||
|
||||
private getCustomDocumentEntry(viewType: string, resource: UriComponents): CustomDocumentStoreEntry {
|
||||
const entry = this._documents.get(viewType, URI.revive(resource));
|
||||
if (!entry) {
|
||||
throw new Error('No custom document found');
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
private getCustomEditorProvider(viewType: string): vscode.CustomEditorProvider {
|
||||
const entry = this._editorProviders.get(viewType);
|
||||
const provider = entry?.provider;
|
||||
if (!provider || !this.supportEditing(provider)) {
|
||||
throw new Error('Custom document is not editable');
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
private supportEditing(
|
||||
provider: vscode.CustomTextEditorProvider | vscode.CustomEditorProvider | vscode.CustomReadonlyEditorProvider
|
||||
): provider is vscode.CustomEditorProvider {
|
||||
return !!(provider as vscode.CustomEditorProvider).onDidChangeCustomDocument;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function isEditEvent(e: vscode.CustomDocumentContentChangeEvent | vscode.CustomDocumentEditEvent): e is vscode.CustomDocumentEditEvent {
|
||||
return typeof (e as vscode.CustomDocumentEditEvent).undo === 'function'
|
||||
&& typeof (e as vscode.CustomDocumentEditEvent).redo === 'function';
|
||||
}
|
||||
|
||||
function hashPath(resource: URI): string {
|
||||
const str = resource.scheme === Schemas.file || resource.scheme === Schemas.untitled ? resource.fsPath : resource.toString();
|
||||
return hash(str) + '';
|
||||
}
|
||||
|
|
@ -3,31 +3,22 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { hash } from 'vs/base/common/hash';
|
||||
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { joinPath } from 'vs/base/common/resources';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
|
||||
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor';
|
||||
import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview';
|
||||
import type * as vscode from 'vscode';
|
||||
import { Cache } from './cache';
|
||||
import * as extHostProtocol from './extHost.protocol';
|
||||
import * as extHostTypes from './extHostTypes';
|
||||
|
||||
type IconPath = URI | { light: URI, dark: URI };
|
||||
|
||||
export class ExtHostWebview implements vscode.Webview {
|
||||
|
||||
readonly #handle: extHostProtocol.WebviewPanelHandle;
|
||||
|
@ -125,7 +116,10 @@ export class ExtHostWebview implements vscode.Webview {
|
|||
}
|
||||
}
|
||||
|
||||
export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPanel {
|
||||
type IconPath = URI | { light: URI, dark: URI };
|
||||
|
||||
|
||||
class ExtHostWebviewPanel extends Disposable implements vscode.WebviewPanel {
|
||||
|
||||
readonly #handle: extHostProtocol.WebviewPanelHandle;
|
||||
readonly #proxy: extHostProtocol.MainThreadWebviewsShape;
|
||||
|
@ -274,137 +268,6 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa
|
|||
}
|
||||
}
|
||||
|
||||
class CustomDocumentStoreEntry {
|
||||
|
||||
private _backupCounter = 1;
|
||||
|
||||
constructor(
|
||||
public readonly document: vscode.CustomDocument,
|
||||
private readonly _storagePath: URI | undefined,
|
||||
) { }
|
||||
|
||||
private readonly _edits = new Cache<vscode.CustomDocumentEditEvent>('custom documents');
|
||||
|
||||
private _backup?: vscode.CustomDocumentBackup;
|
||||
|
||||
addEdit(item: vscode.CustomDocumentEditEvent): number {
|
||||
return this._edits.add([item]);
|
||||
}
|
||||
|
||||
async undo(editId: number, isDirty: boolean): Promise<void> {
|
||||
await this.getEdit(editId).undo();
|
||||
if (!isDirty) {
|
||||
this.disposeBackup();
|
||||
}
|
||||
}
|
||||
|
||||
async redo(editId: number, isDirty: boolean): Promise<void> {
|
||||
await this.getEdit(editId).redo();
|
||||
if (!isDirty) {
|
||||
this.disposeBackup();
|
||||
}
|
||||
}
|
||||
|
||||
disposeEdits(editIds: number[]): void {
|
||||
for (const id of editIds) {
|
||||
this._edits.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
getNewBackupUri(): URI {
|
||||
if (!this._storagePath) {
|
||||
throw new Error('Backup requires a valid storage path');
|
||||
}
|
||||
const fileName = hashPath(this.document.uri) + (this._backupCounter++);
|
||||
return joinPath(this._storagePath, fileName);
|
||||
}
|
||||
|
||||
updateBackup(backup: vscode.CustomDocumentBackup): void {
|
||||
this._backup?.delete();
|
||||
this._backup = backup;
|
||||
}
|
||||
|
||||
disposeBackup(): void {
|
||||
this._backup?.delete();
|
||||
this._backup = undefined;
|
||||
}
|
||||
|
||||
private getEdit(editId: number): vscode.CustomDocumentEditEvent {
|
||||
const edit = this._edits.get(editId, 0);
|
||||
if (!edit) {
|
||||
throw new Error('No edit found');
|
||||
}
|
||||
return edit;
|
||||
}
|
||||
}
|
||||
|
||||
class CustomDocumentStore {
|
||||
private readonly _documents = new Map<string, CustomDocumentStoreEntry>();
|
||||
|
||||
public get(viewType: string, resource: vscode.Uri): CustomDocumentStoreEntry | undefined {
|
||||
return this._documents.get(this.key(viewType, resource));
|
||||
}
|
||||
|
||||
public add(viewType: string, document: vscode.CustomDocument, storagePath: URI | undefined): CustomDocumentStoreEntry {
|
||||
const key = this.key(viewType, document.uri);
|
||||
if (this._documents.has(key)) {
|
||||
throw new Error(`Document already exists for viewType:${viewType} resource:${document.uri}`);
|
||||
}
|
||||
const entry = new CustomDocumentStoreEntry(document, storagePath);
|
||||
this._documents.set(key, entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
public delete(viewType: string, document: vscode.CustomDocument) {
|
||||
const key = this.key(viewType, document.uri);
|
||||
this._documents.delete(key);
|
||||
}
|
||||
|
||||
private key(viewType: string, resource: vscode.Uri): string {
|
||||
return `${viewType}@@@${resource}`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const enum WebviewEditorType {
|
||||
Text,
|
||||
Custom
|
||||
}
|
||||
|
||||
type ProviderEntry = {
|
||||
readonly extension: IExtensionDescription;
|
||||
readonly type: WebviewEditorType.Text;
|
||||
readonly provider: vscode.CustomTextEditorProvider;
|
||||
} | {
|
||||
readonly extension: IExtensionDescription;
|
||||
readonly type: WebviewEditorType.Custom;
|
||||
readonly provider: vscode.CustomReadonlyEditorProvider;
|
||||
};
|
||||
|
||||
class EditorProviderStore {
|
||||
private readonly _providers = new Map<string, ProviderEntry>();
|
||||
|
||||
public addTextProvider(viewType: string, extension: IExtensionDescription, provider: vscode.CustomTextEditorProvider): vscode.Disposable {
|
||||
return this.add(WebviewEditorType.Text, viewType, extension, provider);
|
||||
}
|
||||
|
||||
public addCustomProvider(viewType: string, extension: IExtensionDescription, provider: vscode.CustomReadonlyEditorProvider): vscode.Disposable {
|
||||
return this.add(WebviewEditorType.Custom, viewType, extension, provider);
|
||||
}
|
||||
|
||||
public get(viewType: string): ProviderEntry | undefined {
|
||||
return this._providers.get(viewType);
|
||||
}
|
||||
|
||||
private add(type: WebviewEditorType, viewType: string, extension: IExtensionDescription, provider: vscode.CustomTextEditorProvider | vscode.CustomReadonlyEditorProvider): vscode.Disposable {
|
||||
if (this._providers.has(viewType)) {
|
||||
throw new Error(`Provider for viewType:${viewType} already registered`);
|
||||
}
|
||||
this._providers.set(viewType, { type, extension, provider } as ProviderEntry);
|
||||
return new extHostTypes.Disposable(() => this._providers.delete(viewType));
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
||||
|
||||
private static newHandle(): extHostProtocol.WebviewPanelHandle {
|
||||
|
@ -414,25 +277,19 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
|||
private readonly _proxy: extHostProtocol.MainThreadWebviewsShape;
|
||||
|
||||
private readonly _webviews = new Map<extHostProtocol.WebviewPanelHandle, ExtHostWebview>();
|
||||
private readonly _webviewPanels = new Map<extHostProtocol.WebviewPanelHandle, ExtHostWebviewEditor>();
|
||||
private readonly _webviewPanels = new Map<extHostProtocol.WebviewPanelHandle, ExtHostWebviewPanel>();
|
||||
|
||||
private readonly _serializers = new Map<string, {
|
||||
readonly serializer: vscode.WebviewPanelSerializer;
|
||||
readonly extension: IExtensionDescription;
|
||||
}>();
|
||||
|
||||
private readonly _editorProviders = new EditorProviderStore();
|
||||
|
||||
private readonly _documents = new CustomDocumentStore();
|
||||
|
||||
constructor(
|
||||
mainContext: extHostProtocol.IMainContext,
|
||||
private readonly initData: WebviewInitData,
|
||||
private readonly workspace: IExtHostWorkspace | undefined,
|
||||
private readonly _logService: ILogService,
|
||||
private readonly _deprecationService: IExtHostApiDeprecationService,
|
||||
private readonly _extHostDocuments: ExtHostDocuments,
|
||||
private readonly _extensionStoragePaths?: IExtensionStoragePaths,
|
||||
) {
|
||||
this._proxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadWebviews);
|
||||
}
|
||||
|
@ -454,8 +311,8 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
|||
this._proxy.$createWebviewPanel(toExtensionData(extension), handle, viewType, title, webviewShowOptions, convertWebviewOptions(extension, this.workspace, options));
|
||||
|
||||
const webview = this.createNewWebview(handle, options, extension);
|
||||
const panel = new ExtHostWebviewEditor(handle, this._proxy, viewType, title, viewColumn, options, webview);
|
||||
this._webviewPanels.set(handle, panel);
|
||||
const panel = this.createNewWebviewPanel(handle, viewType, title, viewColumn, options, webview);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
|
@ -477,43 +334,6 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
|||
});
|
||||
}
|
||||
|
||||
public registerCustomEditorProvider(
|
||||
extension: IExtensionDescription,
|
||||
viewType: string,
|
||||
provider: vscode.CustomReadonlyEditorProvider | vscode.CustomTextEditorProvider,
|
||||
options: { webviewOptions?: vscode.WebviewPanelOptions, supportsMultipleEditorsPerDocument?: boolean },
|
||||
): vscode.Disposable {
|
||||
const disposables = new DisposableStore();
|
||||
if ('resolveCustomTextEditor' in provider) {
|
||||
disposables.add(this._editorProviders.addTextProvider(viewType, extension, provider));
|
||||
this._proxy.$registerTextEditorProvider(toExtensionData(extension), viewType, options.webviewOptions || {}, {
|
||||
supportsMove: !!provider.moveCustomTextEditor,
|
||||
});
|
||||
} else {
|
||||
disposables.add(this._editorProviders.addCustomProvider(viewType, extension, provider));
|
||||
|
||||
if (this.supportEditing(provider)) {
|
||||
disposables.add(provider.onDidChangeCustomDocument(e => {
|
||||
const entry = this.getCustomDocumentEntry(viewType, e.document.uri);
|
||||
if (isEditEvent(e)) {
|
||||
const editId = entry.addEdit(e);
|
||||
this._proxy.$onDidEdit(e.document.uri, viewType, editId, e.label);
|
||||
} else {
|
||||
this._proxy.$onContentChange(e.document.uri, viewType);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
this._proxy.$registerCustomEditorProvider(toExtensionData(extension), viewType, options.webviewOptions || {}, !!options.supportsMultipleEditorsPerDocument);
|
||||
}
|
||||
|
||||
return extHostTypes.Disposable.from(
|
||||
disposables,
|
||||
new extHostTypes.Disposable(() => {
|
||||
this._proxy.$unregisterEditorProvider(viewType);
|
||||
}));
|
||||
}
|
||||
|
||||
public $onMessage(
|
||||
handle: extHostProtocol.WebviewPanelHandle,
|
||||
message: any
|
||||
|
@ -587,151 +407,14 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
|||
const { serializer, extension } = entry;
|
||||
|
||||
const webview = this.createNewWebview(webviewHandle, options, extension);
|
||||
const revivedPanel = new ExtHostWebviewEditor(webviewHandle, this._proxy, viewType, title, typeof position === 'number' && position >= 0 ? typeConverters.ViewColumn.to(position) : undefined, options, webview);
|
||||
this._webviewPanels.set(webviewHandle, revivedPanel);
|
||||
const revivedPanel = this.createNewWebviewPanel(webviewHandle, viewType, title, position, options, webview);
|
||||
await serializer.deserializeWebviewPanel(revivedPanel, state);
|
||||
}
|
||||
|
||||
async $createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, cancellation: CancellationToken) {
|
||||
const entry = this._editorProviders.get(viewType);
|
||||
if (!entry) {
|
||||
throw new Error(`No provider found for '${viewType}'`);
|
||||
}
|
||||
|
||||
if (entry.type !== WebviewEditorType.Custom) {
|
||||
throw new Error(`Invalid provide type for '${viewType}'`);
|
||||
}
|
||||
|
||||
const revivedResource = URI.revive(resource);
|
||||
const document = await entry.provider.openCustomDocument(revivedResource, { backupId }, cancellation);
|
||||
|
||||
let storageRoot: URI | undefined;
|
||||
if (this.supportEditing(entry.provider) && this._extensionStoragePaths) {
|
||||
storageRoot = this._extensionStoragePaths.workspaceValue(entry.extension) ?? this._extensionStoragePaths.globalValue(entry.extension);
|
||||
}
|
||||
this._documents.add(viewType, document, storageRoot);
|
||||
|
||||
return { editable: this.supportEditing(entry.provider) };
|
||||
}
|
||||
|
||||
async $disposeCustomDocument(resource: UriComponents, viewType: string): Promise<void> {
|
||||
const entry = this._editorProviders.get(viewType);
|
||||
if (!entry) {
|
||||
throw new Error(`No provider found for '${viewType}'`);
|
||||
}
|
||||
|
||||
if (entry.type !== WebviewEditorType.Custom) {
|
||||
throw new Error(`Invalid provider type for '${viewType}'`);
|
||||
}
|
||||
|
||||
const revivedResource = URI.revive(resource);
|
||||
const { document } = this.getCustomDocumentEntry(viewType, revivedResource);
|
||||
this._documents.delete(viewType, document);
|
||||
document.dispose();
|
||||
}
|
||||
|
||||
async $resolveWebviewEditor(
|
||||
resource: UriComponents,
|
||||
handle: extHostProtocol.WebviewPanelHandle,
|
||||
viewType: string,
|
||||
title: string,
|
||||
position: EditorViewColumn,
|
||||
options: modes.IWebviewOptions & modes.IWebviewPanelOptions,
|
||||
cancellation: CancellationToken,
|
||||
): Promise<void> {
|
||||
const entry = this._editorProviders.get(viewType);
|
||||
if (!entry) {
|
||||
throw new Error(`No provider found for '${viewType}'`);
|
||||
}
|
||||
|
||||
const webview = this.createNewWebview(handle, options, entry.extension);
|
||||
const revivedPanel = new ExtHostWebviewEditor(handle, this._proxy, viewType, title, typeof position === 'number' && position >= 0 ? typeConverters.ViewColumn.to(position) : undefined, options, webview);
|
||||
this._webviewPanels.set(handle, revivedPanel);
|
||||
|
||||
const revivedResource = URI.revive(resource);
|
||||
|
||||
switch (entry.type) {
|
||||
case WebviewEditorType.Custom:
|
||||
{
|
||||
const { document } = this.getCustomDocumentEntry(viewType, revivedResource);
|
||||
return entry.provider.resolveCustomEditor(document, revivedPanel, cancellation);
|
||||
}
|
||||
case WebviewEditorType.Text:
|
||||
{
|
||||
const document = this._extHostDocuments.getDocument(revivedResource);
|
||||
return entry.provider.resolveCustomTextEditor(document, revivedPanel, cancellation);
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new Error('Unknown webview provider type');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$disposeEdits(resourceComponents: UriComponents, viewType: string, editIds: number[]): void {
|
||||
const document = this.getCustomDocumentEntry(viewType, resourceComponents);
|
||||
document.disposeEdits(editIds);
|
||||
}
|
||||
|
||||
async $onMoveCustomEditor(handle: string, newResourceComponents: UriComponents, viewType: string): Promise<void> {
|
||||
const entry = this._editorProviders.get(viewType);
|
||||
if (!entry) {
|
||||
throw new Error(`No provider found for '${viewType}'`);
|
||||
}
|
||||
|
||||
if (!(entry.provider as vscode.CustomTextEditorProvider).moveCustomTextEditor) {
|
||||
throw new Error(`Provider does not implement move '${viewType}'`);
|
||||
}
|
||||
|
||||
const webview = this.getWebviewPanel(handle);
|
||||
if (!webview) {
|
||||
throw new Error(`No webview found`);
|
||||
}
|
||||
|
||||
const resource = URI.revive(newResourceComponents);
|
||||
const document = this._extHostDocuments.getDocument(resource);
|
||||
await (entry.provider as vscode.CustomTextEditorProvider).moveCustomTextEditor!(document, webview, CancellationToken.None);
|
||||
}
|
||||
|
||||
async $undo(resourceComponents: UriComponents, viewType: string, editId: number, isDirty: boolean): Promise<void> {
|
||||
const entry = this.getCustomDocumentEntry(viewType, resourceComponents);
|
||||
return entry.undo(editId, isDirty);
|
||||
}
|
||||
|
||||
async $redo(resourceComponents: UriComponents, viewType: string, editId: number, isDirty: boolean): Promise<void> {
|
||||
const entry = this.getCustomDocumentEntry(viewType, resourceComponents);
|
||||
return entry.redo(editId, isDirty);
|
||||
}
|
||||
|
||||
async $revert(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void> {
|
||||
const entry = this.getCustomDocumentEntry(viewType, resourceComponents);
|
||||
const provider = this.getCustomEditorProvider(viewType);
|
||||
await provider.revertCustomDocument(entry.document, cancellation);
|
||||
entry.disposeBackup();
|
||||
}
|
||||
|
||||
async $onSave(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void> {
|
||||
const entry = this.getCustomDocumentEntry(viewType, resourceComponents);
|
||||
const provider = this.getCustomEditorProvider(viewType);
|
||||
await provider.saveCustomDocument(entry.document, cancellation);
|
||||
entry.disposeBackup();
|
||||
}
|
||||
|
||||
async $onSaveAs(resourceComponents: UriComponents, viewType: string, targetResource: UriComponents, cancellation: CancellationToken): Promise<void> {
|
||||
const entry = this.getCustomDocumentEntry(viewType, resourceComponents);
|
||||
const provider = this.getCustomEditorProvider(viewType);
|
||||
return provider.saveCustomDocumentAs(entry.document, URI.revive(targetResource), cancellation);
|
||||
}
|
||||
|
||||
async $backup(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise<string> {
|
||||
const entry = this.getCustomDocumentEntry(viewType, resourceComponents);
|
||||
const provider = this.getCustomEditorProvider(viewType);
|
||||
|
||||
const backup = await provider.backupCustomDocument(entry.document, {
|
||||
destination: entry.getNewBackupUri(),
|
||||
}, cancellation);
|
||||
entry.updateBackup(backup);
|
||||
return backup.id;
|
||||
public createNewWebviewPanel(webviewHandle: string, viewType: string, title: string, position: number, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, webview: ExtHostWebview) {
|
||||
const panel = new ExtHostWebviewPanel(webviewHandle, this._proxy, viewType, title, typeof position === 'number' && position >= 0 ? typeConverters.ViewColumn.to(position) : undefined, options, webview);
|
||||
this._webviewPanels.set(webviewHandle, panel);
|
||||
return panel;
|
||||
}
|
||||
|
||||
public createNewWebview(handle: string, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, extension: IExtensionDescription): ExtHostWebview {
|
||||
|
@ -747,35 +430,12 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
|||
return this._webviews.get(handle);
|
||||
}
|
||||
|
||||
private getWebviewPanel(handle: extHostProtocol.WebviewPanelHandle): ExtHostWebviewEditor | undefined {
|
||||
public getWebviewPanel(handle: extHostProtocol.WebviewPanelHandle): ExtHostWebviewPanel | undefined {
|
||||
return this._webviewPanels.get(handle);
|
||||
}
|
||||
|
||||
private getCustomDocumentEntry(viewType: string, resource: UriComponents): CustomDocumentStoreEntry {
|
||||
const entry = this._documents.get(viewType, URI.revive(resource));
|
||||
if (!entry) {
|
||||
throw new Error('No custom document found');
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
private getCustomEditorProvider(viewType: string): vscode.CustomEditorProvider {
|
||||
const entry = this._editorProviders.get(viewType);
|
||||
const provider = entry?.provider;
|
||||
if (!provider || !this.supportEditing(provider)) {
|
||||
throw new Error('Custom document is not editable');
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
private supportEditing(
|
||||
provider: vscode.CustomTextEditorProvider | vscode.CustomEditorProvider | vscode.CustomReadonlyEditorProvider
|
||||
): provider is vscode.CustomEditorProvider {
|
||||
return !!(provider as vscode.CustomEditorProvider).onDidChangeCustomDocument;
|
||||
}
|
||||
}
|
||||
|
||||
function toExtensionData(extension: IExtensionDescription): extHostProtocol.WebviewExtensionDescription {
|
||||
export function toExtensionData(extension: IExtensionDescription): extHostProtocol.WebviewExtensionDescription {
|
||||
return { id: extension.identifier, location: extension.extensionLocation };
|
||||
}
|
||||
|
||||
|
@ -808,13 +468,3 @@ function getDefaultLocalResourceRoots(
|
|||
extension.extensionLocation,
|
||||
];
|
||||
}
|
||||
|
||||
function isEditEvent(e: vscode.CustomDocumentContentChangeEvent | vscode.CustomDocumentEditEvent): e is vscode.CustomDocumentEditEvent {
|
||||
return typeof (e as vscode.CustomDocumentEditEvent).undo === 'function'
|
||||
&& typeof (e as vscode.CustomDocumentEditEvent).redo === 'function';
|
||||
}
|
||||
|
||||
function hashPath(resource: URI): string {
|
||||
const str = resource.scheme === Schemas.file || resource.scheme === Schemas.untitled ? resource.fsPath : resource.toString();
|
||||
return hash(str) + '';
|
||||
}
|
||||
|
|
|
@ -169,7 +169,7 @@ export class ExtHostWebviewViews implements extHostProtocol.ExtHostWebviewViewsS
|
|||
private getWebviewView(handle: string): ExtHostWebviewView {
|
||||
const entry = this._webviewViews.get(handle);
|
||||
if (!entry) {
|
||||
throw new Error('Custom document is not editable');
|
||||
throw new Error('No webview found');
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
|
|
@ -3,33 +3,27 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type * as vscode from 'vscode';
|
||||
import * as assert from 'assert';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { mock } from 'vs/base/test/common/mock';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { MainThreadWebviews } from 'vs/workbench/api/browser/mainThreadWebview';
|
||||
import { ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview';
|
||||
import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor';
|
||||
import { mock } from 'vs/base/test/common/mock';
|
||||
import { SingleProxyRPCProtocol } from './testRPCProtocol';
|
||||
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { IExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { NullApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview';
|
||||
import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor';
|
||||
import type * as vscode from 'vscode';
|
||||
import { SingleProxyRPCProtocol } from './testRPCProtocol';
|
||||
|
||||
suite('ExtHostWebview', () => {
|
||||
|
||||
let rpcProtocol: (IExtHostRpcService & IExtHostContext) | undefined;
|
||||
let extHostDocuments: ExtHostDocuments | undefined;
|
||||
|
||||
setup(() => {
|
||||
const shape = createNoopMainThreadWebviews();
|
||||
rpcProtocol = SingleProxyRPCProtocol(shape);
|
||||
|
||||
const extHostDocumentsAndEditors = new ExtHostDocumentsAndEditors(rpcProtocol, new NullLogService());
|
||||
extHostDocuments = new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors);
|
||||
});
|
||||
|
||||
test('Cannot register multiple serializers for the same view type', async () => {
|
||||
|
@ -39,7 +33,7 @@ suite('ExtHostWebview', () => {
|
|||
webviewCspSource: '',
|
||||
webviewResourceRoot: '',
|
||||
isExtensionDevelopmentDebug: false,
|
||||
}, undefined, new NullLogService(), NullApiDeprecationService, extHostDocuments!);
|
||||
}, undefined, new NullLogService(), NullApiDeprecationService);
|
||||
|
||||
let lastInvokedDeserializer: vscode.WebviewPanelSerializer | undefined = undefined;
|
||||
|
||||
|
@ -76,7 +70,7 @@ suite('ExtHostWebview', () => {
|
|||
webviewCspSource: '',
|
||||
webviewResourceRoot: 'vscode-resource://{{resource}}',
|
||||
isExtensionDevelopmentDebug: false,
|
||||
}, undefined, new NullLogService(), NullApiDeprecationService, extHostDocuments!);
|
||||
}, undefined, new NullLogService(), NullApiDeprecationService);
|
||||
const webview = extHostWebviews.createWebviewPanel({} as any, 'type', 'title', 1, {});
|
||||
|
||||
assert.strictEqual(
|
||||
|
@ -115,7 +109,7 @@ suite('ExtHostWebview', () => {
|
|||
webviewCspSource: '',
|
||||
webviewResourceRoot: `https://{{uuid}}.webview.contoso.com/commit/{{resource}}`,
|
||||
isExtensionDevelopmentDebug: false,
|
||||
}, undefined, new NullLogService(), NullApiDeprecationService, extHostDocuments!);
|
||||
}, undefined, new NullLogService(), NullApiDeprecationService);
|
||||
const webview = extHostWebviews.createWebviewPanel({} as any, 'type', 'title', 1, {});
|
||||
|
||||
function stripEndpointUuid(input: string) {
|
||||
|
|
Loading…
Reference in a new issue