Add proposed webview view API (#104601)

Add proposed webview view API

For #46585

This adds a new `WebviewView` proposed api to VS Code that lets webview be used inside views. Webview views can be contributed using a contribution point such as :

```json
    "views": {
      "explorer": [
        {
          "type": "webview",
          "id": "cats.cat",
          "name": "Cats",
          "visibility": "visible"
        }
      ]
    },
```

* Use proper activation event

* Transparent background

* Fix resize observer

* Adding documentation

* Move webview view to new directory under workbench

* Remove resolver

By moving the webviews view into their own fodler, I was able to avoid the cycle the resolver was originally introduced for

* Use enum in more places

* Hook up title and visible properties for webview views

* Remove test view

* Prefer Thenable

* Add unknown view type error to collector
This commit is contained in:
Matt Bierner
2020-08-20 13:59:22 -07:00
committed by GitHub
parent 038e1a93b9
commit 61f799f53b
12 changed files with 674 additions and 16 deletions

View File

@@ -267,6 +267,92 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa
}
}
export class ExtHostWebviewView extends Disposable implements vscode.WebviewView {
readonly #handle: extHostProtocol.WebviewPanelHandle;
readonly #proxy: extHostProtocol.MainThreadWebviewsShape;
readonly #viewType: string;
readonly #webview: ExtHostWebview;
#isDisposed = false;
#isVisible: boolean;
#title: string | undefined;
constructor(
handle: extHostProtocol.WebviewPanelHandle,
proxy: extHostProtocol.MainThreadWebviewsShape,
viewType: string,
webview: ExtHostWebview,
isVisible: boolean,
) {
super();
this.#viewType = viewType;
this.#handle = handle;
this.#proxy = proxy;
this.#webview = webview;
this.#isVisible = isVisible;
}
public dispose() {
if (this.#isDisposed) {
return;
}
this.#isDisposed = true;
this.#onDidDispose.fire();
super.dispose();
}
readonly #onDidChangeVisibility = this._register(new Emitter<void>());
public readonly onDidChangeVisibility = this.#onDidChangeVisibility.event;
readonly #onDidDispose = this._register(new Emitter<void>());
public readonly onDidDispose = this.#onDidDispose.event;
get title(): string | undefined {
this.assertNotDisposed();
return this.#title;
}
set title(value: string | undefined) {
this.assertNotDisposed();
if (this.#title !== value) {
this.#title = value;
this.#proxy.$setWebviewViewTitle(this.#handle, value);
}
}
get visible() {
return this.#isVisible;
}
get webview() {
return this.#webview;
}
get viewType(): string {
return this.#viewType;
}
_setVisible(visible: boolean) {
if (visible === this.#isVisible) {
return;
}
this.#isVisible = visible;
this.#onDidChangeVisibility.fire();
}
private assertNotDisposed() {
if (this.#isDisposed) {
throw new Error('Webview is disposed');
}
}
}
class CustomDocumentStoreEntry {
private _backupCounter = 1;
@@ -412,7 +498,13 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
readonly extension: IExtensionDescription;
}>();
private readonly _viewProviders = new Map<string, {
readonly provider: vscode.WebviewViewProvider;
readonly extension: IExtensionDescription;
}>();
private readonly _editorProviders = new EditorProviderStore();
private readonly _webviewViews = new Map<extHostProtocol.WebviewPanelHandle, ExtHostWebviewView>();
private readonly _documents = new CustomDocumentStore();
@@ -468,6 +560,27 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
});
}
public registerWebviewViewProvider(
extension: IExtensionDescription,
viewType: string,
provider: vscode.WebviewViewProvider,
webviewOptions?: {
retainContextWhenHidden?: boolean
},
): vscode.Disposable {
if (this._viewProviders.has(viewType)) {
throw new Error(`View provider for '${viewType}' already registered`);
}
this._viewProviders.set(viewType, { provider, extension });
this._proxy.$registerWebviewViewProvider(viewType, webviewOptions);
return new extHostTypes.Disposable(() => {
this._viewProviders.delete(viewType);
this._proxy.$unregisterSerializer(viewType);
});
}
public registerCustomEditorProvider(
extension: IExtensionDescription,
viewType: string,
@@ -583,6 +696,41 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
await serializer.deserializeWebviewPanel(revivedPanel, state);
}
async $resolveWebviewView(
webviewHandle: string,
viewType: string,
state: any,
cancellation: CancellationToken,
): Promise<void> {
const entry = this._viewProviders.get(viewType);
if (!entry) {
throw new Error(`No view provider found for '${viewType}'`);
}
const { provider, extension } = entry;
const webview = new ExtHostWebview(webviewHandle, this._proxy, reviveOptions({ /* todo */ }), this.initData, this.workspace, extension, this._deprecationService);
const revivedView = new ExtHostWebviewView(webviewHandle, this._proxy, viewType, webview, true);
this._webviewViews.set(webviewHandle, revivedView);
await provider.resolveWebviewView(revivedView, { state }, cancellation);
}
async $onDidChangeWebviewViewVisibility(
webviewHandle: string,
visible: boolean
) {
const webviewView = this.getWebviewView(webviewHandle);
webviewView._setVisible(visible);
}
async $disposeWebviewView(webviewHandle: string) {
const webviewView = this.getWebviewView(webviewHandle);
this._webviewViews.delete(webviewHandle);
webviewView.dispose();
}
async $createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, cancellation: CancellationToken) {
const entry = this._editorProviders.get(viewType);
if (!entry) {
@@ -746,6 +894,14 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
return provider;
}
private getWebviewView(handle: string): ExtHostWebviewView {
const entry = this._webviewViews.get(handle);
if (!entry) {
throw new Error('Custom document is not editable');
}
return entry;
}
private supportEditing(
provider: vscode.CustomTextEditorProvider | vscode.CustomEditorProvider | vscode.CustomReadonlyEditorProvider
): provider is vscode.CustomEditorProvider {