From 44b75883624f88e9af21e55c22397512aa2af9cd Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 13 Feb 2019 12:41:05 +0100 Subject: [PATCH] remove getNamedCustomer, connect MainThreadWebview with CodeInsetController --- src/vs/vscode.proposed.d.ts | 2 +- .../api/electron-browser/mainThreadWebview.ts | 111 ++++++++++++------ src/vs/workbench/api/node/extHost.api.impl.ts | 2 +- src/vs/workbench/api/node/extHost.protocol.ts | 12 +- .../api/node/extHostLanguageFeatures.ts | 49 ++++---- src/vs/workbench/api/node/extHostWebview.ts | 10 +- .../codeinset/codeInset.contribution.ts | 68 +++++------ .../workbench/contrib/codeinset/codeInset.ts | 6 +- .../contrib/codeinset/codeInsetWidget.ts | 37 ++---- .../electron-browser/webviewEditorService.ts | 27 +---- .../services/extensions/common/extensions.ts | 5 +- .../electron-browser/extensionService.ts | 5 - 12 files changed, 165 insertions(+), 169 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index ed37fac7b1d..2cfff0cea1a 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -51,7 +51,7 @@ declare module 'vscode' { export interface CodeInsetProvider { onDidChangeCodeInsets?: Event; provideCodeInsets(document: TextDocument, token: CancellationToken): ProviderResult; - resolveCodeInset?(codeInset: CodeInset, webview: Webview, token: CancellationToken): ProviderResult; + resolveCodeInset(codeInset: CodeInset, webview: Webview, token: CancellationToken): ProviderResult; } export namespace languages { diff --git a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts index 7a88fc7e389..03b880ecec2 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts @@ -8,7 +8,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelShowOptions } from 'vs/workbench/api/node/extHost.protocol'; +import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelShowOptions, WebviewInsetHandle } from 'vs/workbench/api/node/extHost.protocol'; import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/shared/editor'; import { WebviewEditor } from 'vs/workbench/contrib/webview/electron-browser/webviewEditor'; import { WebviewEditorInput } from 'vs/workbench/contrib/webview/electron-browser/webviewEditorInput'; @@ -21,7 +21,11 @@ import { extHostNamedCustomer } from './extHostCustomers'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { onUnexpectedError } from 'vs/base/common/errors'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; - +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/webviewElement'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { CodeInsetController } from 'vs/workbench/contrib/codeinset/codeInset.contribution'; +import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; @extHostNamedCustomer(MainContext.MainThreadWebviews) export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviver { @@ -38,6 +42,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv private readonly _proxy: ExtHostWebviewsShape; private readonly _webviews = new Map(); + private readonly _webviewsElements = new Map(); private readonly _revivers = new Set(); private _activeWebview: WebviewPanelHandle | undefined = undefined; @@ -50,7 +55,10 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv @IWebviewEditorService private readonly _webviewService: IWebviewEditorService, @IOpenerService private readonly _openerService: IOpenerService, @IExtensionService private readonly _extensionService: IExtensionService, - @ITelemetryService private readonly _telemetryService: ITelemetryService + @ITelemetryService private readonly _telemetryService: ITelemetryService, + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @ICodeEditorService private readonly _codeEditorService: ICodeEditorService, + @IPartService private readonly _partService: IPartService, ) { this._proxy = context.getProxy(ExtHostContext.ExtHostWebviews); _editorService.onDidActiveEditorChange(this.onActiveEditorChanged, this, this._toDispose); @@ -67,7 +75,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv this._toDispose = dispose(this._toDispose); } - public $createWebview( + public $createWebviewPanel( handle: WebviewPanelHandle, viewType: string, title: string, @@ -99,21 +107,42 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv this._telemetryService.publicLog('webviews:createWebviewPanel', { extensionId: extensionId.value }); } - public createInsetWebview( - handle: WebviewPanelHandle, - parent: HTMLElement, - options: WebviewInputOptions, - extensionLocation: UriComponents - ): WebviewEditorInput { - const webview = this._webviewService.createInsetWebview(parent, reviveWebviewOptions(options), URI.revive(extensionLocation), this.createWebviewEventDelegate(handle)); - webview.state = { - viewType: webview.viewType, - state: undefined - }; + $createWebviewCodeInset(handle: WebviewInsetHandle, symbolId: string, options: vscode.WebviewOptions, extensionLocation: UriComponents): void { + // todo@joh main is for the lack of a code-inset service + // which we maybe wanna have... this is how it now works + // 1) create webview element + // 2) find the code inset controller that request it + // 3) let the controller adopt the widget + // 4) continue to forward messages to the webview + const webview = this._instantiationService.createInstance( + WebviewElement, + this._partService.getContainer(Parts.EDITOR_PART), + { + useSameOriginForRoot: true, + extensionLocation: URI.revive(extensionLocation) + }, + { + allowScripts: options.enableScripts + } + ); + + let found = false; + for (const editor of this._codeEditorService.listCodeEditors()) { + const ctrl = CodeInsetController.get(editor); + if (ctrl && ctrl.acceptWebview(symbolId, webview)) { + found = true; + break; + } + } - this._webviews.set(handle, webview); - this._activeWebview = handle; - return webview; + if (!found) { + webview.dispose(); + return; + } + // this will leak... the adopted webview will be disposed by the + // code inset controller. we might need a dispose-event here so that + // we can clean up things. + this._webviewsElements.set(handle, webview); } public $disposeWebview(handle: WebviewPanelHandle): void { @@ -131,14 +160,22 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv webview.iconPath = reviveWebviewIcon(value); } - public $setHtml(handle: WebviewPanelHandle, value: string): void { - const webview = this.getWebview(handle); - webview.html = value; + public $setHtml(handle: WebviewPanelHandle | WebviewInsetHandle, value: string): void { + if (typeof handle === 'number') { + this._webviewsElements.get(handle).contents = value; + } else { + const webview = this.getWebview(handle); + webview.html = value; + } } - public $setOptions(handle: WebviewPanelHandle, options: vscode.WebviewOptions): void { - const webview = this.getWebview(handle); - webview.setOptions(reviveWebviewOptions(options)); + public $setOptions(handle: WebviewPanelHandle | WebviewInsetHandle, options: vscode.WebviewOptions): void { + if (typeof handle === 'number') { + this._webviewsElements.get(handle).options = reviveWebviewOptions(options); + } else { + const webview = this.getWebview(handle); + webview.setOptions(reviveWebviewOptions(options)); + } } public $reveal(handle: WebviewPanelHandle, showOptions: WebviewPanelShowOptions): void { @@ -152,18 +189,24 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv this._webviewService.revealWebview(webview, targetGroup || this._editorGroupService.getGroup(webview.group), !!showOptions.preserveFocus); } - public $postMessage(handle: WebviewPanelHandle, message: any): Promise { - const webview = this.getWebview(handle); - const editors = this._editorService.visibleControls - .filter(e => e instanceof WebviewEditor) - .map(e => e as WebviewEditor) - .filter(e => e.input!.matches(webview)); + public $postMessage(handle: WebviewPanelHandle | WebviewInsetHandle, message: any): Promise { + if (typeof handle === 'number') { + this._webviewsElements.get(handle).sendMessage(message); + return Promise.resolve(true); - for (const editor of editors) { - editor.sendMessage(message); - } + } else { + const webview = this.getWebview(handle); + const editors = this._editorService.visibleControls + .filter(e => e instanceof WebviewEditor) + .map(e => e as WebviewEditor) + .filter(e => e.input!.matches(webview)); + + for (const editor of editors) { + editor.sendMessage(message); + } - return Promise.resolve(editors.length > 0); + return Promise.resolve(editors.length > 0); + } } public $registerSerializer(viewType: string): void { diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index a3dc698c3b0..c590299823d 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -478,7 +478,7 @@ export function createApiFactory( return extHostOutputService.createOutputChannel(name); }, createWebviewPanel(viewType: string, title: string, showOptions: vscode.ViewColumn | { viewColumn: vscode.ViewColumn, preserveFocus?: boolean }, options: vscode.WebviewPanelOptions & vscode.WebviewOptions): vscode.WebviewPanel { - return extHostWebviews.createWebview(extension, viewType, title, showOptions, options); + return extHostWebviews.createWebviewPanel(extension, viewType, title, showOptions, options); }, createTerminal(nameOrOptions: vscode.TerminalOptions | string, shellPath?: string, shellArgs?: string[]): vscode.Terminal { if (typeof nameOrOptions === 'object') { diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index de93ffd6f8e..dadca886aa0 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -469,20 +469,24 @@ export interface MainThreadTelemetryShape extends IDisposable { export type WebviewPanelHandle = string; +export type WebviewInsetHandle = number; + export interface WebviewPanelShowOptions { readonly viewColumn?: EditorViewColumn; readonly preserveFocus?: boolean; } export interface MainThreadWebviewsShape extends IDisposable { - $createWebview(handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: vscode.WebviewPanelOptions & vscode.WebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): void; + $createWebviewPanel(handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: vscode.WebviewPanelOptions & vscode.WebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): void; + $createWebviewCodeInset(handle: WebviewInsetHandle, symbolId: string, options: vscode.WebviewOptions, extensionLocation: UriComponents): void; $disposeWebview(handle: WebviewPanelHandle): void; $reveal(handle: WebviewPanelHandle, showOptions: WebviewPanelShowOptions): void; $setTitle(handle: WebviewPanelHandle, value: string): void; $setIconPath(handle: WebviewPanelHandle, value: { light: UriComponents, dark: UriComponents } | undefined): void; - $setHtml(handle: WebviewPanelHandle, value: string): void; - $setOptions(handle: WebviewPanelHandle, options: vscode.WebviewOptions): void; - $postMessage(handle: WebviewPanelHandle, value: any): Promise; + + $setHtml(handle: WebviewPanelHandle | WebviewInsetHandle, value: string): void; + $setOptions(handle: WebviewPanelHandle | WebviewInsetHandle, options: vscode.WebviewOptions): void; + $postMessage(handle: WebviewPanelHandle | WebviewInsetHandle, value: any): Promise; $registerSerializer(viewType: string): void; $unregisterSerializer(viewType: string): void; diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index 906da259dea..15a14ef8e6e 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -15,7 +15,7 @@ import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments'; import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHostCommands'; import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics'; import { asPromise } from 'vs/base/common/async'; -import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, WorkspaceSymbolDto, SuggestResultDto, WorkspaceSymbolsDto, SuggestionDto, CodeActionDto, ISerializedDocumentFilter, WorkspaceEditDto, ISerializedSignatureHelpProviderMetadata, LinkDto, CodeLensDto, MainThreadWebviewsShape } from './extHost.protocol'; +import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, WorkspaceSymbolDto, SuggestResultDto, WorkspaceSymbolsDto, SuggestionDto, CodeActionDto, ISerializedDocumentFilter, WorkspaceEditDto, ISerializedSignatureHelpProviderMetadata, LinkDto, CodeLensDto, MainThreadWebviewsShape, CodeInsetDto } from './extHost.protocol'; import { regExpLeadsToEndlessLoop, regExpFlags } from 'vs/base/common/strings'; import { IPosition } from 'vs/editor/common/core/position'; import { IRange, Range as EditorRange } from 'vs/editor/common/core/range'; @@ -28,6 +28,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { ExtHostWebview } from 'vs/workbench/api/node/extHostWebview'; import * as codeInset from 'vs/workbench/contrib/codeinset/codeInset'; +import { generateUuid } from 'vs/base/common/uuid'; // --- adapter @@ -154,38 +155,33 @@ class CodeInsetAdapter { private readonly _provider: vscode.CodeInsetProvider ) { } - provideCodeInsets(resource: URI, token: CancellationToken): Promise { + provideCodeInsets(resource: URI, token: CancellationToken): Promise { const doc = this._documents.getDocumentData(resource).document; return asPromise(() => this._provider.provideCodeInsets(doc, token)).then(insets => { if (Array.isArray(insets)) { return insets.map(inset => { - const id = this._heapService.keep(inset); - return ObjectIdentifier.mixin({ + const $ident = this._heapService.keep(inset); + const id = generateUuid(); + return { + $ident, + id, range: typeConvert.Range.from(inset.range), height: inset.height - }, id); + }; }); - } else { - return undefined; } + return undefined; }); } - resolveCodeInset(symbol: codeInset.ICodeInsetSymbol, webview: vscode.Webview, token: CancellationToken): Promise { + resolveCodeInset(symbol: CodeInsetDto, webview: vscode.Webview, token: CancellationToken): Promise { const inset = this._heapService.get(ObjectIdentifier.of(symbol)); if (!inset) { - return undefined; - } - - let resolve: Promise; - if (typeof this._provider.resolveCodeInset !== 'function') { - resolve = Promise.resolve(inset); - } else { - resolve = asPromise(() => this._provider.resolveCodeInset(inset, webview, token)); + return Promise.resolve(symbol); } - return resolve.then(newInset => { + return asPromise(() => this._provider.resolveCodeInset(inset, webview, token)).then(newInset => { newInset = newInset || inset; return symbol; }); @@ -1057,7 +1053,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return ExtHostLanguageFeatures._handlePool++; } - private _withAdapter(handle: number, ctor: { new(...args: any[]): A }, callback: (adapter: A) => Promise): Promise { + private _withAdapter(handle: number, ctor: { new(...args: any[]): A }, callback: (adapter: A, extenson: IExtensionDescription) => Promise): Promise { const data = this._adapter.get(handle); if (!data) { return Promise.reject(new Error('no adapter found')); @@ -1069,7 +1065,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { t1 = Date.now(); this._logService.trace(`[${data.extension.identifier.value}] INVOKE provider '${(ctor as any).name}'`); } - let p = callback(data.adapter); + let p = callback(data.adapter, data.extension); const extension = data.extension; if (extension) { Promise.resolve(p).then( @@ -1156,14 +1152,13 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._withAdapter(handle, CodeInsetAdapter, adapter => adapter.provideCodeInsets(URI.revive(resource), token)); } - $resolveCodeInset(handle: number, resource: UriComponents, symbol: codeInset.ICodeInsetSymbol, token: CancellationToken): Promise { - const webview = new ExtHostWebview(symbol.webviewHandle, this._webviewProxy, { enableScripts: true }); - webview.html = ''; - const x = this._withAdapter(handle, CodeInsetAdapter, adapter => adapter.resolveCodeInset(symbol, webview, token)); - return x; - } - - $createCodeInsetWebview(handle: number) { + $resolveCodeInset(handle: number, _resource: UriComponents, symbol: codeInset.ICodeInsetSymbol, token: CancellationToken): Promise { + const webviewHandle = Math.random(); + const webview = new ExtHostWebview(webviewHandle, this._webviewProxy, { enableScripts: true }); + return this._withAdapter(handle, CodeInsetAdapter, async (adapter, extension) => { + await this._webviewProxy.$createWebviewCodeInset(webviewHandle, symbol.id, { enableCommandUris: true, enableScripts: true }, extension.extensionLocation); + return adapter.resolveCodeInset(symbol, webview, token); + }); } // --- declaration diff --git a/src/vs/workbench/api/node/extHostWebview.ts b/src/vs/workbench/api/node/extHostWebview.ts index 2dfa8440244..65fab620a36 100644 --- a/src/vs/workbench/api/node/extHostWebview.ts +++ b/src/vs/workbench/api/node/extHostWebview.ts @@ -8,14 +8,14 @@ import { URI } from 'vs/base/common/uri'; import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters'; import { EditorViewColumn } from 'vs/workbench/api/shared/editor'; import * as vscode from 'vscode'; -import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewState } from './extHost.protocol'; +import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewState, WebviewInsetHandle } from './extHost.protocol'; import { Disposable } from './extHostTypes'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; type IconPath = URI | { light: URI, dark: URI }; export class ExtHostWebview implements vscode.Webview { - private readonly _handle: WebviewPanelHandle; + private readonly _handle: WebviewPanelHandle | WebviewInsetHandle; private readonly _proxy: MainThreadWebviewsShape; private _html: string; private _options: vscode.WebviewOptions; @@ -25,7 +25,7 @@ export class ExtHostWebview implements vscode.Webview { public readonly onDidReceiveMessage: Event = this._onMessageEmitter.event; constructor( - handle: WebviewPanelHandle, + handle: WebviewPanelHandle | WebviewInsetHandle, proxy: MainThreadWebviewsShape, options: vscode.WebviewOptions ) { @@ -243,7 +243,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { this._proxy = mainContext.getProxy(MainContext.MainThreadWebviews); } - public createWebview( + public createWebviewPanel( extension: IExtensionDescription, viewType: string, title: string, @@ -257,7 +257,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { }; const handle = ExtHostWebviews.newHandle(); - this._proxy.$createWebview(handle, viewType, title, webviewShowOptions, options, extension.identifier, extension.extensionLocation); + this._proxy.$createWebviewPanel(handle, viewType, title, webviewShowOptions, options, extension.identifier, extension.extensionLocation); const webview = new ExtHostWebview(handle, this._proxy, options); const panel = new ExtHostWebviewPanel(handle, this._proxy, viewType, title, viewColumn, options, webview); diff --git a/src/vs/workbench/contrib/codeinset/codeInset.contribution.ts b/src/vs/workbench/contrib/codeinset/codeInset.contribution.ts index 7e20339e1de..57e84679755 100644 --- a/src/vs/workbench/contrib/codeinset/codeInset.contribution.ts +++ b/src/vs/workbench/contrib/codeinset/codeInset.contribution.ts @@ -12,18 +12,19 @@ import * as editorCommon from 'vs/editor/common/editorCommon'; import { IModelDecorationsChangeAccessor } from 'vs/editor/common/model'; import { CodeInsetProviderRegistry, getCodeInsetData, ICodeInsetData } from './codeInset'; import { CodeInsetWidget, CodeInsetHelper } from './codeInsetWidget'; -import { ICommandService } from 'vs/platform/commands/common/commands'; -import { INotificationService } from 'vs/platform/notification/common/notification'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { MainThreadWebviews } from 'vs/workbench/api/electron-browser/mainThreadWebview'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import { localize } from 'vs/nls.mock'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/webviewElement'; export class CodeInsetController implements editorCommon.IEditorContribution { + static get(editor: editorBrowser.ICodeEditor): CodeInsetController { + return editor.getContribution(CodeInsetController.ID); + } + private static readonly ID: string = 'css.editor.codeInset'; private _isEnabled: boolean; @@ -31,17 +32,14 @@ export class CodeInsetController implements editorCommon.IEditorContribution { private _globalToDispose: IDisposable[]; private _localToDispose: IDisposable[]; private _insetWidgets: CodeInsetWidget[]; + private _pendingWebviews = new Map any>(); private _currentFindCodeInsetSymbolsPromise: CancelablePromise; private _modelChangeCounter: number; private _currentResolveCodeInsetSymbolsPromise: CancelablePromise; private _detectVisibleInsets: RunOnceScheduler; - private _mainThreadWebviews: MainThreadWebviews; constructor( private _editor: editorBrowser.ICodeEditor, - @ICommandService private readonly _commandService: ICommandService, - @INotificationService private readonly _notificationService: INotificationService, - @IExtensionService private readonly _extensionService: IExtensionService, @IConfigurationService private readonly _configService: IConfigurationService, ) { this._isEnabled = this._configService.getValue('editor.codeInsets'); @@ -72,6 +70,15 @@ export class CodeInsetController implements editorCommon.IEditorContribution { this._globalToDispose = dispose(this._globalToDispose); } + acceptWebview(symbolId: string, webviewElement: WebviewElement): boolean { + if (this._pendingWebviews.has(symbolId)) { + this._pendingWebviews.get(symbolId)(webviewElement); + this._pendingWebviews.delete(symbolId); + return true; + } + return false; + } + private _localDispose(): void { if (this._currentFindCodeInsetSymbolsPromise) { this._currentFindCodeInsetSymbolsPromise.cancel(); @@ -239,11 +246,11 @@ export class CodeInsetController implements editorCommon.IEditorContribution { groupsIndex++; codeInsetIndex++; } else { - this._insetWidgets.splice(codeInsetIndex, 0, - new CodeInsetWidget(groups[groupsIndex], - this._editor, helper, - this._commandService, this._notificationService, - () => this._detectVisibleInsets.schedule())); + this._insetWidgets.splice( + codeInsetIndex, + 0, + new CodeInsetWidget(groups[groupsIndex], this._editor, helper) + ); codeInsetIndex++; groupsIndex++; } @@ -257,11 +264,10 @@ export class CodeInsetController implements editorCommon.IEditorContribution { // Create extra symbols while (groupsIndex < groups.length) { - this._insetWidgets.push( - new CodeInsetWidget(groups[groupsIndex], - this._editor, helper, - this._commandService, this._notificationService, - () => this._detectVisibleInsets.schedule())); + this._insetWidgets.push(new CodeInsetWidget( + groups[groupsIndex], + this._editor, helper + )); groupsIndex++; } @@ -272,15 +278,6 @@ export class CodeInsetController implements editorCommon.IEditorContribution { scrollState.restore(this._editor); } - - private getWebviewService(): MainThreadWebviews { - if (!this._mainThreadWebviews) { - this._mainThreadWebviews = this._extensionService.getNamedCustomer('MainThreadWebviews'); - } - return this._mainThreadWebviews; - } - - private _onViewportChanged(): void { if (this._currentResolveCodeInsetSymbolsPromise) { this._currentResolveCodeInsetSymbolsPromise.cancel(); @@ -311,13 +308,18 @@ export class CodeInsetController implements editorCommon.IEditorContribution { const allPromises = allWidgetRequests.map((widgetRequests, r) => { const widgetPromises = widgetRequests.map(request => { - const symbol = request.symbol; - if (!symbol.webviewHandle && typeof request.provider.resolveCodeInset === 'function') { - const mainThreadWebviews = this.getWebviewService(); - symbol.webviewHandle = insetWidgets[r].createWebview(mainThreadWebviews, request.provider.extensionLocation); - return request.provider.resolveCodeInset(model, symbol, token); + if (request.resolved) { + return Promise.resolve(void 0); } - return Promise.resolve(void 0); + let a = new Promise(resolve => { + this._pendingWebviews.set(request.symbol.id, element => { + request.resolved = true; + insetWidgets[r].adoptWebview(element); + resolve(); + }); + }); + let b = request.provider.resolveCodeInset(model, request.symbol, token); + return Promise.all([a, b]); }); return Promise.all(widgetPromises); diff --git a/src/vs/workbench/contrib/codeinset/codeInset.ts b/src/vs/workbench/contrib/codeinset/codeInset.ts index 2301776cb18..36a8ab933e4 100644 --- a/src/vs/workbench/contrib/codeinset/codeInset.ts +++ b/src/vs/workbench/contrib/codeinset/codeInset.ts @@ -16,17 +16,16 @@ import { ProviderResult } from 'vs/editor/common/modes'; import { IRange } from 'vs/editor/common/core/range'; export interface ICodeInsetSymbol { + id: string; range: IRange; - id?: string; height?: number; - webviewHandle?: string; } export interface CodeInsetProvider { onDidChange?: Event; extensionLocation: UriComponents; provideCodeInsets(model: ITextModel, token: CancellationToken): ProviderResult; - resolveCodeInset?(model: ITextModel, codeInset: ICodeInsetSymbol, token: CancellationToken): ProviderResult; + resolveCodeInset(model: ITextModel, codeInset: ICodeInsetSymbol, token: CancellationToken): ProviderResult; } export const CodeInsetProviderRegistry = new LanguageFeatureRegistry(); @@ -34,6 +33,7 @@ export const CodeInsetProviderRegistry = new LanguageFeatureRegistry { diff --git a/src/vs/workbench/contrib/codeinset/codeInsetWidget.ts b/src/vs/workbench/contrib/codeinset/codeInsetWidget.ts index dca58609fdc..12562860a9a 100644 --- a/src/vs/workbench/contrib/codeinset/codeInsetWidget.ts +++ b/src/vs/workbench/contrib/codeinset/codeInsetWidget.ts @@ -4,16 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./codeInsetWidget'; -import { ICommandService } from 'vs/platform/commands/common/commands'; import { Range } from 'vs/editor/common/core/range'; import * as editorBrowser from 'vs/editor/browser/editorBrowser'; import { ICodeInsetData } from './codeInset'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { IModelDeltaDecoration, IModelDecorationsChangeAccessor, ITextModel } from 'vs/editor/common/model'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import { WebviewEditorInput } from 'vs/workbench/contrib/webview/electron-browser/webviewEditorInput'; -import { MainThreadWebviews } from 'vs/workbench/api/electron-browser/mainThreadWebview'; -import { UriComponents } from 'vs/base/common/uri'; +import { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/webviewElement'; export interface IDecorationIdCallback { @@ -52,21 +48,17 @@ export class CodeInsetHelper { export class CodeInsetWidget { private readonly _editor: editorBrowser.ICodeEditor; + private _webview: WebviewElement; private _viewZone: editorBrowser.IViewZone; private _viewZoneId?: number = undefined; private _decorationIds: string[]; private _data: ICodeInsetData[]; - private _webview: WebviewEditorInput | undefined; - private _webviewHandle: string | undefined; private _range: Range; constructor( data: ICodeInsetData[], // all the insets on the same line (often just one) editor: editorBrowser.ICodeEditor, - helper: CodeInsetHelper, - commandService: ICommandService, - notificationService: INotificationService, - updateCallabck: Function + helper: CodeInsetHelper ) { this._editor = editor; this._data = data; @@ -89,7 +81,6 @@ export class CodeInsetWidget { } public dispose(helper: CodeInsetHelper, viewZoneChangeAccessor: editorBrowser.IViewZoneChangeAccessor): void { - console.log('DISPOSE'); while (this._decorationIds.length) { helper.removeDecoration(this._decorationIds.pop()); } @@ -97,6 +88,9 @@ export class CodeInsetWidget { viewZoneChangeAccessor.removeZone(this._viewZoneId); this._viewZone = undefined; } + if (this._webview) { + this._webview.dispose(); + } } public isValid(): boolean { @@ -140,25 +134,17 @@ export class CodeInsetWidget { return -1; } - public get webview() { return this._webview; } - - static webviewPool = 1; - - public createWebview(mainThreadWebviews: MainThreadWebviews, extensionLocation: UriComponents) { - if (this._webviewHandle) { return this._webviewHandle; } + public adoptWebview(webview: WebviewElement): void { const lineNumber = this._range.endLineNumber; this._editor.changeViewZones(accessor => { + if (this._viewZoneId) { - this._webview.dispose(); accessor.removeZone(this._viewZoneId); + this._webview.dispose(); } - const div = document.createElement('div'); - - this._webviewHandle = CodeInsetWidget.webviewPool++ + ''; - this._webview = mainThreadWebviews.createInsetWebview(this._webviewHandle, div, { enableScripts: true }, extensionLocation); - const webview = this._webview.webview; + const div = document.createElement('div'); webview.mountTo(div); webview.onMessage((e: { type: string, payload: any }) => { // The webview contents can use a "size-info" message to report its size. @@ -170,15 +156,14 @@ export class CodeInsetWidget { }); } }); - this._viewZone = { afterLineNumber: lineNumber, heightInPx: 50, domNode: div }; this._viewZoneId = accessor.addZone(this._viewZone); + this._webview = webview; }); - return this._webviewHandle; } public reposition(viewZoneChangeAccessor: editorBrowser.IViewZoneChangeAccessor): void { diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewEditorService.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewEditorService.ts index 3bc86337748..bbe24ca44d0 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewEditorService.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewEditorService.ts @@ -12,8 +12,6 @@ import * as vscode from 'vscode'; import { WebviewEditorInput } from './webviewEditorInput'; import { GroupIdentifier } from 'vs/workbench/common/editor'; import { equals } from 'vs/base/common/arrays'; -import { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/webviewElement'; -import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; export const IWebviewEditorService = createDecorator('webviewEditorService'); @@ -34,13 +32,6 @@ export interface IWebviewEditorService { events: WebviewEvents ): WebviewEditorInput; - createInsetWebview( - parent: HTMLElement, - options: vscode.WebviewOptions, - extensionLocation: URI, - events: WebviewEvents - ): WebviewEditorInput; - reviveWebview( viewType: string, id: number, @@ -105,8 +96,7 @@ export class WebviewEditorService implements IWebviewEditorService { constructor( @IEditorService private readonly _editorService: IEditorService, @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, - @IPartService private readonly _partService: IPartService, + @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService ) { } createWebview( @@ -122,21 +112,6 @@ export class WebviewEditorService implements IWebviewEditorService { return webviewInput; } - createInsetWebview( - parent: HTMLElement, - options: vscode.WebviewOptions, - extensionLocation: URI, - events: WebviewEvents - ): WebviewEditorInput { - const webviewEditorInput = this._instantiationService.createInstance(WebviewEditorInput, 'codeinset', undefined, '', options, {}, events, extensionLocation, undefined); - webviewEditorInput.webview = this._instantiationService.createInstance(WebviewElement, - this._partService.getContainer(Parts.EDITOR_PART), - { allowSvgs: true, useSameOriginForRoot: true }, - { allowScripts: true, disableFindView: true }); - webviewEditorInput.webview.mountTo(parent); - return webviewEditorInput; - } - revealWebview( webview: WebviewEditorInput, group: IEditorGroup, diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index 247fcde06ff..bab03582819 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -222,9 +222,6 @@ export interface IExtensionService extends ICpuProfilerTarget { * Stops the extension host. */ stopExtensionHost(): void; - - getNamedCustomer?(sid: string): any; - } export interface ICpuProfilerTarget { @@ -285,4 +282,4 @@ export class NullExtensionService implements IExtensionService { stopExtensionHost(): void { } canAddExtension(): boolean { return false; } canRemoveExtension(): boolean { return false; } -} \ No newline at end of file +} diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 2dd7b5fe86b..d7d69bb07c9 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -417,11 +417,6 @@ export class ExtensionService extends Disposable implements IExtensionService { this._stopExtensionHostProcess(); } - public getNamedCustomer(sid: string): any { - const ncs = this._extensionHostProcessManagers.map(m => m.getNamedCustomer(sid)).filter(c => c); - return ncs.length ? ncs[0] : undefined; - } - private _stopExtensionHostProcess(): void { let previouslyActivatedExtensionIds: ExtensionIdentifier[] = []; this._extensionHostActiveExtensions.forEach((value) => { -- GitLab