From 6f1da34c2efc68c8145061e072e053625107306e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 4 Jun 2019 12:18:06 +0200 Subject: [PATCH] debt - decouple webviews from code insets, move things to /browser/-layer, change inset api proposal to push style, re #66418 --- build/lib/i18n.resources.json | 4 - src/vs/vscode.proposed.d.ts | 28 +- .../extensionHost.contribution.common.ts | 70 --- .../api/browser/extensionHost.contribution.ts | 67 ++- .../api/browser/mainThreadCodeInsets.ts | 144 ++++++ .../api/browser/mainThreadLanguageFeatures.ts | 32 +- .../api/browser/mainThreadWebview.ts | 369 ++++++++++++++- .../workbench/api/common/extHost.protocol.ts | 31 +- .../workbench/api/common/extHostCodeInsets.ts | 130 ++++++ .../api/common/extHostLanguageFeatures.ts | 81 +--- src/vs/workbench/api/common/extHostWebview.ts | 6 +- .../extensionHost.contribution.ts | 7 - .../api/electron-browser/mainThreadWebview.ts | 437 ------------------ src/vs/workbench/api/node/extHost.api.impl.ts | 10 +- .../contrib/codeinset/common/codeInset.ts | 70 --- .../codeInset.contribution.ts | 353 -------------- .../electron-browser/codeInsetWidget.css | 8 - .../electron-browser/codeInsetWidget.ts | 193 -------- .../webview/browser/webviewEditorInput.ts | 2 +- .../api/extHostWebview.test.ts | 2 +- src/vs/workbench/workbench.main.ts | 5 +- src/vs/workbench/workbench.web.main.ts | 3 - 22 files changed, 726 insertions(+), 1326 deletions(-) delete mode 100644 src/vs/workbench/api/browser/extensionHost.contribution.common.ts create mode 100644 src/vs/workbench/api/browser/mainThreadCodeInsets.ts create mode 100644 src/vs/workbench/api/common/extHostCodeInsets.ts delete mode 100644 src/vs/workbench/api/electron-browser/extensionHost.contribution.ts delete mode 100644 src/vs/workbench/api/electron-browser/mainThreadWebview.ts delete mode 100644 src/vs/workbench/contrib/codeinset/common/codeInset.ts delete mode 100644 src/vs/workbench/contrib/codeinset/electron-browser/codeInset.contribution.ts delete mode 100644 src/vs/workbench/contrib/codeinset/electron-browser/codeInsetWidget.css delete mode 100644 src/vs/workbench/contrib/codeinset/electron-browser/codeInsetWidget.ts diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index 22d3d165c3c33..32b0e6fb4c799 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -38,10 +38,6 @@ "name": "vs/workbench/contrib/codeEditor", "project": "vscode-workbench" }, - { - "name": "vs/workbench/contrib/codeinset", - "project": "vscode-workbench" - }, { "name": "vs/workbench/contrib/callHierarchy", "project": "vscode-workbench" diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 177aeee3353e5..966803bd86cf6 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -137,32 +137,20 @@ declare module 'vscode' { // #region Joh - code insets - /** - */ - export class CodeInset { - range: Range; - height?: number; - constructor(range: Range, height?: number); - } - - export interface CodeInsetProvider { - onDidChangeCodeInsets?: Event; - provideCodeInsets(document: TextDocument, token: CancellationToken): ProviderResult; - resolveCodeInset(codeInset: CodeInset, webview: Webview, token: CancellationToken): ProviderResult; + export interface WebviewEditorInset { + readonly editor: TextEditor; + readonly range: Range; + readonly webview: Webview; + readonly onDidDispose: Event; + dispose(): void; } - export namespace languages { - - /** - * Register a code inset provider. - * - */ - export function registerCodeInsetProvider(selector: DocumentSelector, provider: CodeInsetProvider): Disposable; + export namespace window { + export function createWebviewTextEditorInset(editor: TextEditor, range: Range, options?: WebviewOptions): WebviewEditorInset; } //#endregion - //#region Joh - read/write in chunks export interface FileSystemProvider { diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.common.ts b/src/vs/workbench/api/browser/extensionHost.contribution.common.ts deleted file mode 100644 index 38a2c92226554..0000000000000 --- a/src/vs/workbench/api/browser/extensionHost.contribution.common.ts +++ /dev/null @@ -1,70 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; - -// --- other interested parties -import { JSONValidationExtensionPoint } from 'vs/workbench/api/common/jsonValidationExtensionPoint'; -import { ColorExtensionPoint } from 'vs/workbench/services/themes/common/colorExtensionPoint'; -import { LanguageConfigurationFileHandler } from 'vs/workbench/contrib/codeEditor/browser/languageConfigurationExtensionPoint'; - -// --- mainThread participants -import './mainThreadClipboard'; -import './mainThreadCommands'; -import './mainThreadConfiguration'; -import './mainThreadConsole'; -import './mainThreadDebugService'; -import './mainThreadDecorations'; -import './mainThreadDiagnostics'; -import './mainThreadDialogs'; -import './mainThreadDocumentContentProviders'; -import './mainThreadDocuments'; -import './mainThreadDocumentsAndEditors'; -import './mainThreadEditor'; -import './mainThreadEditors'; -import './mainThreadErrors'; -import './mainThreadExtensionService'; -import './mainThreadFileSystem'; -import './mainThreadFileSystemEventService'; -import './mainThreadHeapService'; -import './mainThreadKeytar'; -import './mainThreadLanguageFeatures'; -import './mainThreadLanguages'; -import './mainThreadLogService'; -import './mainThreadMessageService'; -import './mainThreadOutputService'; -import './mainThreadProgress'; -import './mainThreadQuickOpen'; -import './mainThreadSaveParticipant'; -import './mainThreadSCM'; -import './mainThreadSearch'; -import './mainThreadStatusBar'; -import './mainThreadStorage'; -import './mainThreadTelemetry'; -import './mainThreadTerminalService'; -import './mainThreadTreeViews'; -import './mainThreadUrls'; -import './mainThreadWindow'; -import './mainThreadWorkspace'; -import './mainThreadComments'; -import './mainThreadTask'; -import 'vs/workbench/api/common/apiCommands'; - -export class ExtensionPoints implements IWorkbenchContribution { - - constructor( - @IInstantiationService private readonly instantiationService: IInstantiationService - ) { - // Classes that handle extension points... - this.instantiationService.createInstance(JSONValidationExtensionPoint); - this.instantiationService.createInstance(ColorExtensionPoint); - this.instantiationService.createInstance(LanguageConfigurationFileHandler); - } -} - -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ExtensionPoints, LifecyclePhase.Starting); diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts index a9803561b5f12..b4eca4746c91c 100644 --- a/src/vs/workbench/api/browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts @@ -3,5 +3,70 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import './extensionHost.contribution.common'; +import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; + +// --- other interested parties +import { JSONValidationExtensionPoint } from 'vs/workbench/api/common/jsonValidationExtensionPoint'; +import { ColorExtensionPoint } from 'vs/workbench/services/themes/common/colorExtensionPoint'; +import { LanguageConfigurationFileHandler } from 'vs/workbench/contrib/codeEditor/browser/languageConfigurationExtensionPoint'; + +// --- mainThread participants +import './mainThreadCodeInsets'; +import './mainThreadClipboard'; +import './mainThreadCommands'; +import './mainThreadConfiguration'; +import './mainThreadConsole'; +import './mainThreadDebugService'; +import './mainThreadDecorations'; +import './mainThreadDiagnostics'; +import './mainThreadDialogs'; +import './mainThreadDocumentContentProviders'; +import './mainThreadDocuments'; +import './mainThreadDocumentsAndEditors'; +import './mainThreadEditor'; +import './mainThreadEditors'; +import './mainThreadErrors'; +import './mainThreadExtensionService'; +import './mainThreadFileSystem'; +import './mainThreadFileSystemEventService'; +import './mainThreadHeapService'; +import './mainThreadKeytar'; +import './mainThreadLanguageFeatures'; +import './mainThreadLanguages'; +import './mainThreadLogService'; +import './mainThreadMessageService'; +import './mainThreadOutputService'; +import './mainThreadProgress'; +import './mainThreadQuickOpen'; +import './mainThreadSaveParticipant'; +import './mainThreadSCM'; +import './mainThreadSearch'; +import './mainThreadStatusBar'; +import './mainThreadStorage'; +import './mainThreadTelemetry'; +import './mainThreadTerminalService'; +import './mainThreadTreeViews'; +import './mainThreadUrls'; +import './mainThreadWindow'; import './mainThreadWebview'; +import './mainThreadWorkspace'; +import './mainThreadComments'; +import './mainThreadTask'; +import 'vs/workbench/api/common/apiCommands'; + +export class ExtensionPoints implements IWorkbenchContribution { + + constructor( + @IInstantiationService private readonly instantiationService: IInstantiationService + ) { + // Classes that handle extension points... + this.instantiationService.createInstance(JSONValidationExtensionPoint); + this.instantiationService.createInstance(ColorExtensionPoint); + this.instantiationService.createInstance(LanguageConfigurationFileHandler); + } +} + +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ExtensionPoints, LifecyclePhase.Starting); diff --git a/src/vs/workbench/api/browser/mainThreadCodeInsets.ts b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts new file mode 100644 index 0000000000000..49a1abd1c12f4 --- /dev/null +++ b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts @@ -0,0 +1,144 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { UriComponents, URI } from 'vs/base/common/uri'; +import * as modes from 'vs/editor/common/modes'; +import { MainContext, MainThreadEditorInsetsShape, IExtHostContext, ExtHostEditorInsetsShape, ExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; +import { extHostNamedCustomer } from '../common/extHostCustomers'; +import { IRange } from 'vs/editor/common/core/range'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { IWebviewService, Webview } from 'vs/workbench/contrib/webview/common/webview'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { IActiveCodeEditor, IViewZone } from 'vs/editor/browser/editorBrowser'; + +// todo@joh move these things back into something like contrib/insets +class EditorWebviewZone implements IViewZone { + + readonly domNode: HTMLElement; + readonly afterLineNumber: number; + readonly afterColumn: number; + readonly heightInLines: number; + + private _id: number; + // suppressMouseDown?: boolean | undefined; + // heightInPx?: number | undefined; + // minWidthInPx?: number | undefined; + // marginDomNode?: HTMLElement | null | undefined; + // onDomNodeTop?: ((top: number) => void) | undefined; + // onComputedHeight?: ((height: number) => void) | undefined; + + constructor( + readonly editor: IActiveCodeEditor, + readonly range: IRange, + readonly webview: Webview, + ) { + this.domNode = document.createElement('div'); + this.afterLineNumber = range.startLineNumber; + this.afterColumn = range.startColumn; + this.heightInLines = range.endLineNumber - range.startLineNumber; + + editor.changeViewZones(accessor => this._id = accessor.addZone(this)); + webview.mountTo(this.domNode); + } + + dispose(): void { + this.editor.changeViewZones(accessor => accessor.removeZone(this._id)); + } +} + +@extHostNamedCustomer(MainContext.MainThreadEditorInsets) +export class MainThreadEditorInsets implements MainThreadEditorInsetsShape { + + private readonly _proxy: ExtHostEditorInsetsShape; + private readonly _disposables = new DisposableStore(); + private readonly _insets = new Map(); + + constructor( + context: IExtHostContext, + @ICodeEditorService private readonly _editorService: ICodeEditorService, + @IWebviewService private readonly _webviewService: IWebviewService, + ) { + this._proxy = context.getProxy(ExtHostContext.ExtHostEditorInsets); + } + + dispose(): void { + this._disposables.dispose(); + } + + async $createEditorInset(handle: number, id: string, uri: UriComponents, range: IRange, options: modes.IWebviewOptions): Promise { + + let editor: IActiveCodeEditor | undefined; + id = id.substr(0, id.indexOf(',')); //todo@joh HACK + + for (const candidate of this._editorService.listCodeEditors()) { + if (candidate.getId() === id && candidate.hasModel() && candidate.getModel()!.uri.toString() === URI.revive(uri).toString()) { + editor = candidate; + break; + } + } + + if (!editor) { + setTimeout(() => this._proxy.$onDidDispose(handle)); + return; + } + + const disposables = new DisposableStore(); + + const webview = this._webviewService.createWebview({ + enableFindWidget: false, + allowSvgs: false, + extension: undefined + }, { + allowScripts: options.enableScripts + }); + + const webviewZone = new EditorWebviewZone(editor, range, webview); + + const remove = () => { + disposables.dispose(); + this._proxy.$onDidDispose(handle); + this._insets.delete(handle); + }; + + disposables.add(editor.onDidChangeModel(remove)); + disposables.add(editor.onDidDispose(remove)); + disposables.add(webviewZone); + disposables.add(webview); + disposables.add(webview.onMessage(msg => this._proxy.$onDidReceiveMessage(handle, msg))); + + this._insets.set(handle, webviewZone); + } + + $disposeEditorInset(handle: number): void { + const inset = this._insets.get(handle); + if (inset) { + this._insets.delete(handle); + inset.dispose(); + } + } + + $setHtml(handle: number, value: string): void { + const inset = this._insets.get(handle); + if (inset) { + inset.webview.html = value; + } + } + + $setOptions(handle: number, options: modes.IWebviewOptions): void { + const inset = this._insets.get(handle); + if (inset) { + inset.webview.options = options; + } + } + + $postMessage(handle: number, value: any): Promise { + const inset = this._insets.get(handle); + if (inset) { + inset.webview.sendMessage(value); + return Promise.resolve(true); + } + return Promise.resolve(false); + } +} diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 126112ed91713..474bde4c500d5 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -11,14 +11,13 @@ import * as search from 'vs/workbench/contrib/search/common/search'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Position as EditorPosition } from 'vs/editor/common/core/position'; import { Range as EditorRange, IRange } from 'vs/editor/common/core/range'; -import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ISerializedLanguageConfiguration, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, LocationDto, WorkspaceSymbolDto, CodeActionDto, reviveWorkspaceEditDto, ISerializedDocumentFilter, DefinitionLinkDto, ISerializedSignatureHelpProviderMetadata, CodeInsetDto, LinkDto, CallHierarchyDto, SuggestDataDto } from '../common/extHost.protocol'; +import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ISerializedLanguageConfiguration, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, LocationDto, WorkspaceSymbolDto, CodeActionDto, reviveWorkspaceEditDto, ISerializedDocumentFilter, DefinitionLinkDto, ISerializedSignatureHelpProviderMetadata, LinkDto, CallHierarchyDto, SuggestDataDto } from '../common/extHost.protocol'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { LanguageConfiguration, IndentationRule, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration'; import { IModeService } from 'vs/editor/common/services/modeService'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { URI } from 'vs/base/common/uri'; import { Selection } from 'vs/editor/common/core/selection'; -import * as codeInset from 'vs/workbench/contrib/codeinset/common/codeInset'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import * as callh from 'vs/workbench/contrib/callHierarchy/common/callHierarchy'; import { IHeapService } from 'vs/workbench/services/heap/common/heap'; @@ -178,35 +177,6 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha } } - // -- code inset - - $registerCodeInsetSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number): void { - - const provider = { - provideCodeInsets: (model: ITextModel, token: CancellationToken): CodeInsetDto[] | Thenable => { - return this._proxy.$provideCodeInsets(handle, model.uri, token).then(dto => { - if (dto) { dto.forEach(obj => this._heapService.trackObject(obj)); } - return dto; - }); - }, - resolveCodeInset: (model: ITextModel, codeInset: CodeInsetDto, token: CancellationToken): CodeInsetDto | Thenable => { - return this._proxy.$resolveCodeInset(handle, model.uri, codeInset, token).then(obj => { - this._heapService.trackObject(obj); - return obj; - }); - } - }; - - if (typeof eventHandle === 'number') { - const emitter = new Emitter(); - this._registrations[eventHandle] = emitter; - provider.onDidChange = emitter.event; - } - - const langSelector = selector; - this._registrations[handle] = codeInset.CodeInsetProviderRegistry.register(langSelector, provider); - } - // --- declaration $registerDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void { diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index a1867465520a8..c7b1f6462c56e 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -3,46 +3,365 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable } from 'vs/base/common/lifecycle'; -import { UriComponents } from 'vs/base/common/uri'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import * as map from 'vs/base/common/map'; +import { URI, UriComponents } from 'vs/base/common/uri'; import * as modes from 'vs/editor/common/modes'; +import { localize } from 'vs/nls'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { MainContext, MainThreadWebviewsShape, WebviewPanelShowOptions } from 'vs/workbench/api/common/extHost.protocol'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelShowOptions } from 'vs/workbench/api/common/extHost.protocol'; +import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; +import { WebviewEditor } from 'vs/workbench/contrib/webview/browser/webviewEditor'; +import { WebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; +import { ICreateWebViewShowOptions, IWebviewEditorService, WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewEditorService'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { ACTIVE_GROUP, IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { extHostNamedCustomer } from '../common/extHostCustomers'; +import { IProductService } from 'vs/platform/product/common/product'; @extHostNamedCustomer(MainContext.MainThreadWebviews) export class MainThreadWebviews extends Disposable implements MainThreadWebviewsShape { - $createWebviewPanel(handle: string, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: modes.IWebviewPanelOptions & modes.IWebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): void { - throw new Error('Method not implemented.'); + + private static readonly standardSupportedLinkSchemes = new Set([ + 'http', + 'https', + 'mailto', + 'vscode', + 'vscode-insider', + ]); + + private static revivalPool = 0; + + + private readonly _proxy: ExtHostWebviewsShape; + private readonly _webviews = new Map(); + private readonly _revivers = new Map(); + + private _activeWebview: WebviewPanelHandle | undefined = undefined; + + constructor( + context: IExtHostContext, + @ILifecycleService lifecycleService: ILifecycleService, + @IExtensionService extensionService: IExtensionService, + @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, + @IEditorService private readonly _editorService: IEditorService, + @IWebviewEditorService private readonly _webviewService: IWebviewEditorService, + @IOpenerService private readonly _openerService: IOpenerService, + @ITelemetryService private readonly _telemetryService: ITelemetryService, + @IProductService private readonly _productService: IProductService, + ) { + super(); + + this._proxy = context.getProxy(ExtHostContext.ExtHostWebviews); + this._register(_editorService.onDidActiveEditorChange(this.onActiveEditorChanged, this)); + this._register(_editorService.onDidVisibleEditorsChange(this.onVisibleEditorsChanged, this)); + + // This reviver's only job is to activate webview extensions + // This should trigger the real reviver to be registered from the extension host side. + this._register(_webviewService.registerReviver({ + canRevive: (webview) => { + const viewType = webview.state.viewType; + if (viewType) { + extensionService.activateByEvent(`onWebviewPanel:${viewType}`); + } + return false; + }, + reviveWebview: () => { throw new Error('not implemented'); } + })); + + this._register(lifecycleService.onBeforeShutdown(e => { + e.veto(this._onBeforeShutdown()); + }, this)); + } + + public $createWebviewPanel( + handle: WebviewPanelHandle, + viewType: string, + title: string, + showOptions: { viewColumn?: EditorViewColumn, preserveFocus?: boolean }, + options: WebviewInputOptions, + extensionId: ExtensionIdentifier, + extensionLocation: UriComponents + ): void { + const mainThreadShowOptions: ICreateWebViewShowOptions = Object.create(null); + if (showOptions) { + mainThreadShowOptions.preserveFocus = !!showOptions.preserveFocus; + mainThreadShowOptions.group = viewColumnToEditorGroup(this._editorGroupService, showOptions.viewColumn); + } + + const webview = this._webviewService.createWebview(this.getInternalWebviewId(viewType), title, mainThreadShowOptions, reviveWebviewOptions(options), { + location: URI.revive(extensionLocation), + id: extensionId + }, this.createWebviewEventDelegate(handle)); + webview.state = { + viewType: viewType, + state: undefined + }; + + this._webviews.set(handle, webview); + + /* __GDPR__ + "webviews:createWebviewPanel" : { + "extensionId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + this._telemetryService.publicLog('webviews:createWebviewPanel', { extensionId: extensionId.value }); + } + + public $disposeWebview(handle: WebviewPanelHandle): void { + const webview = this.getWebview(handle); + webview.dispose(); + } + + public $setTitle(handle: WebviewPanelHandle, value: string): void { + const webview = this.getWebview(handle); + webview.setName(value); + } + + public $setIconPath(handle: WebviewPanelHandle, value: { light: UriComponents, dark: UriComponents } | undefined): void { + const webview = this.getWebview(handle); + webview.iconPath = reviveWebviewIcon(value); + } + + public $setHtml(handle: WebviewPanelHandle, value: string): void { + const webview = this.getWebview(handle); + webview.html = value; + } + + public $setOptions(handle: WebviewPanelHandle, options: modes.IWebviewOptions): void { + const webview = this.getWebview(handle); + webview.setOptions(reviveWebviewOptions(options as any /*todo@mat */)); + } + + public $reveal(handle: WebviewPanelHandle, showOptions: WebviewPanelShowOptions): void { + const webview = this.getWebview(handle); + if (webview.isDisposed()) { + return; + } + + const targetGroup = this._editorGroupService.getGroup(viewColumnToEditorGroup(this._editorGroupService, showOptions.viewColumn)) || this._editorGroupService.getGroup(webview.group || 0); + if (targetGroup) { + this._webviewService.revealWebview(webview, targetGroup, !!showOptions.preserveFocus); + } + } + + public async $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)); + + if (editors.length > 0) { + editors[0].sendMessage(message); + return true; + } + + if (webview.webview) { + webview.webview.sendMessage(message); + return true; + } + + return false; } - $createWebviewCodeInset(handle: number, symbolId: string, options: modes.IWebviewOptions, extensionId: ExtensionIdentifier | undefined, extensionLocation: UriComponents | undefined): void { - throw new Error('Method not implemented.'); + + public $registerSerializer(viewType: string): void { + if (this._revivers.has(viewType)) { + throw new Error(`Reviver for ${viewType} already registered`); + } + + this._revivers.set(viewType, this._webviewService.registerReviver({ + canRevive: (webview) => { + return webview.state && webview.state.viewType === viewType; + }, + reviveWebview: async (webview): Promise => { + const viewType = webview.state.viewType; + const handle = 'revival-' + MainThreadWebviews.revivalPool++; + this._webviews.set(handle, webview); + webview._events = this.createWebviewEventDelegate(handle); + let state = undefined; + if (webview.state.state) { + try { + state = JSON.parse(webview.state.state); + } catch { + // noop + } + } + + try { + await this._proxy.$deserializeWebviewPanel(handle, viewType, webview.getTitle(), state, editorGroupToViewColumn(this._editorGroupService, webview.group || 0), webview.options); + } catch (error) { + onUnexpectedError(error); + webview.html = MainThreadWebviews.getDeserializationFailedContents(viewType); + } + } + })); } - $disposeWebview(handle: string): void { - throw new Error('Method not implemented.'); + + public $unregisterSerializer(viewType: string): void { + const reviver = this._revivers.get(viewType); + if (!reviver) { + throw new Error(`No reviver for ${viewType} registered`); + } + + reviver.dispose(); + this._revivers.delete(viewType); } - $reveal(handle: string, showOptions: WebviewPanelShowOptions): void { - throw new Error('Method not implemented.'); + + private getInternalWebviewId(viewType: string): string { + return `mainThreadWebview-${viewType}`; } - $setTitle(handle: string, value: string): void { - throw new Error('Method not implemented.'); + + private _onBeforeShutdown(): boolean { + this._webviews.forEach((webview) => { + if (!webview.isDisposed() && webview.state && this._revivers.has(webview.state.viewType)) { + webview.state.state = webview.webviewState; + } + }); + return false; // Don't veto shutdown } - $setIconPath(handle: string, value: { light: UriComponents; dark: UriComponents; } | undefined): void { - throw new Error('Method not implemented.'); + + private createWebviewEventDelegate(handle: WebviewPanelHandle) { + return { + onDidClickLink: (uri: URI) => this.onDidClickLink(handle, uri), + onMessage: (message: any) => this._proxy.$onMessage(handle, message), + onDispose: () => { + this._proxy.$onDidDisposeWebviewPanel(handle).finally(() => { + this._webviews.delete(handle); + }); + } + }; } - $setHtml(handle: string | number, value: string): void { - throw new Error('Method not implemented.'); + + private onActiveEditorChanged() { + const activeEditor = this._editorService.activeControl; + let newActiveWebview: { input: WebviewEditorInput, handle: WebviewPanelHandle } | undefined = undefined; + if (activeEditor && activeEditor.input instanceof WebviewEditorInput) { + for (const handle of map.keys(this._webviews)) { + const input = this._webviews.get(handle)!; + if (input.matches(activeEditor.input)) { + newActiveWebview = { input, handle }; + break; + } + } + } + + if (newActiveWebview && newActiveWebview.handle === this._activeWebview) { + // Webview itself unchanged but position may have changed + this._proxy.$onDidChangeWebviewPanelViewState(newActiveWebview.handle, { + active: true, + visible: true, + position: editorGroupToViewColumn(this._editorGroupService, newActiveWebview.input.group || 0) + }); + return; + } + + // Broadcast view state update for currently active + if (typeof this._activeWebview !== 'undefined') { + const oldActiveWebview = this._webviews.get(this._activeWebview); + if (oldActiveWebview) { + this._proxy.$onDidChangeWebviewPanelViewState(this._activeWebview, { + active: false, + visible: this._editorService.visibleControls.some(editor => !!editor.input && editor.input.matches(oldActiveWebview)), + position: editorGroupToViewColumn(this._editorGroupService, oldActiveWebview.group || 0), + }); + } + } + + // Then for newly active + if (newActiveWebview) { + this._proxy.$onDidChangeWebviewPanelViewState(newActiveWebview.handle, { + active: true, + visible: true, + position: editorGroupToViewColumn(this._editorGroupService, activeEditor ? activeEditor.group : ACTIVE_GROUP), + }); + this._activeWebview = newActiveWebview.handle; + } else { + this._activeWebview = undefined; + } } - $setOptions(handle: string | number, options: modes.IWebviewOptions): void { - throw new Error('Method not implemented.'); + + private onVisibleEditorsChanged(): void { + this._webviews.forEach((input, handle) => { + for (const workbenchEditor of this._editorService.visibleControls) { + if (workbenchEditor.input && workbenchEditor.input.matches(input)) { + const editorPosition = editorGroupToViewColumn(this._editorGroupService, workbenchEditor.group!); + + input.updateGroup(workbenchEditor.group!.id); + this._proxy.$onDidChangeWebviewPanelViewState(handle, { + active: handle === this._activeWebview, + visible: true, + position: editorPosition + }); + break; + } + } + }); + } + + private onDidClickLink(handle: WebviewPanelHandle, link: URI): void { + if (!link) { + return; + } + + const webview = this.getWebview(handle); + if (this.isSupportedLink(webview, link)) { + this._openerService.open(link); + } + } + + private isSupportedLink(webview: WebviewEditorInput, link: URI): boolean { + if (MainThreadWebviews.standardSupportedLinkSchemes.has(link.scheme)) { + return true; + } + if (this._productService.urlProtocol === link.scheme) { + return true; + } + return !!webview.options.enableCommandUris && link.scheme === 'command'; } - $postMessage(handle: string | number, value: any): Promise { - throw new Error('Method not implemented.'); + + private getWebview(handle: WebviewPanelHandle): WebviewEditorInput { + const webview = this._webviews.get(handle); + if (!webview) { + throw new Error('Unknown webview handle:' + handle); + } + return webview; } - $registerSerializer(viewType: string): void { - throw new Error('Method not implemented.'); + + private static getDeserializationFailedContents(viewType: string) { + return ` + + + + + + + ${localize('errorMessage', "An error occurred while restoring view:{0}", viewType)} + `; } - $unregisterSerializer(viewType: string): void { - throw new Error('Method not implemented.'); +} + +function reviveWebviewOptions(options: WebviewInputOptions): WebviewInputOptions { + return { + ...options, + localResourceRoots: Array.isArray(options.localResourceRoots) ? options.localResourceRoots.map(r => URI.revive(r)) : undefined, + }; +} + +function reviveWebviewIcon( + value: { light: UriComponents, dark: UriComponents } | undefined +): { light: URI, dark: URI } | undefined { + if (!value) { + return undefined; } + + return { + light: URI.revive(value.light), + dark: URI.revive(value.dark) + }; } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 8379c584038a8..ed5ae5e51a5ce 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -42,7 +42,6 @@ import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { ResolvedAuthority, RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import * as codeInset from 'vs/workbench/contrib/codeinset/common/codeInset'; import * as callHierarchy from 'vs/workbench/contrib/callHierarchy/common/callHierarchy'; import { IRelativePattern } from 'vs/base/common/glob'; import { IRemoteConsoleLog } from 'vs/base/common/console'; @@ -333,7 +332,6 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable { $unregister(handle: number): void; $registerDocumentSymbolProvider(handle: number, selector: ISerializedDocumentFilter[], label: string): void; $registerCodeLensSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number | undefined): void; - $registerCodeInsetSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number | undefined): void; $emitCodeLensEvent(eventHandle: number, event?: any): void; $registerDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void; $registerDeclarationSupport(handle: number, selector: ISerializedDocumentFilter[]): void; @@ -510,9 +508,21 @@ export interface MainThreadTelemetryShape extends IDisposable { $publicLog(eventName: string, data?: any): void; } -export type WebviewPanelHandle = string; +export interface MainThreadEditorInsetsShape extends IDisposable { + $createEditorInset(handle: number, editorId: string, document: UriComponents, range: IRange, options: modes.IWebviewOptions): Promise; + $disposeEditorInset(handle: number): void; + + $setHtml(handle: number, value: string): void; + $setOptions(handle: number, options: modes.IWebviewOptions): void; + $postMessage(handle: number, value: any): Promise; +} -export type WebviewInsetHandle = number; +export interface ExtHostEditorInsetsShape { + $onDidDispose(handle: number): void; + $onDidReceiveMessage(handle: number, message: any): void; +} + +export type WebviewPanelHandle = string; export interface WebviewPanelShowOptions { readonly viewColumn?: EditorViewColumn; @@ -521,15 +531,14 @@ export interface WebviewPanelShowOptions { export interface MainThreadWebviewsShape extends IDisposable { $createWebviewPanel(handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: modes.IWebviewPanelOptions & modes.IWebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): void; - $createWebviewCodeInset(handle: WebviewInsetHandle, symbolId: string, options: modes.IWebviewOptions, extensionId: ExtensionIdentifier | undefined, extensionLocation: UriComponents | undefined): 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 | WebviewInsetHandle, value: string): void; - $setOptions(handle: WebviewPanelHandle | WebviewInsetHandle, options: modes.IWebviewOptions): void; - $postMessage(handle: WebviewPanelHandle | WebviewInsetHandle, value: any): Promise; + $setHtml(handle: WebviewPanelHandle, value: string): void; + $setOptions(handle: WebviewPanelHandle, options: modes.IWebviewOptions): void; + $postMessage(handle: WebviewPanelHandle, value: any): Promise; $registerSerializer(viewType: string): void; $unregisterSerializer(viewType: string): void; @@ -1005,8 +1014,6 @@ export interface CodeLensDto extends ObjectIdentifier { command?: CommandDto; } -export type CodeInsetDto = ObjectIdentifier & codeInset.ICodeInsetSymbol; - export interface CallHierarchyDto { _id: number; kind: modes.SymbolKind; @@ -1021,8 +1028,6 @@ export interface ExtHostLanguageFeaturesShape { $provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise; $provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise; $resolveCodeLens(handle: number, symbol: CodeLensDto, token: CancellationToken): Promise; - $provideCodeInsets(handle: number, resource: UriComponents, token: CancellationToken): Promise; - $resolveCodeInset(handle: number, resource: UriComponents, symbol: CodeInsetDto, token: CancellationToken): Promise; $provideDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; $provideDeclaration(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; $provideImplementation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; @@ -1248,6 +1253,7 @@ export const MainContext = { MainThreadDocuments: createMainId('MainThreadDocuments'), MainThreadDocumentContentProviders: createMainId('MainThreadDocumentContentProviders'), MainThreadTextEditors: createMainId('MainThreadTextEditors'), + MainThreadEditorInsets: createMainId('MainThreadEditorInsets'), MainThreadErrors: createMainId('MainThreadErrors'), MainThreadTreeViews: createMainId('MainThreadTreeViews'), MainThreadKeytar: createMainId('MainThreadKeytar'), @@ -1298,6 +1304,7 @@ export const ExtHostContext = { ExtHostWorkspace: createExtId('ExtHostWorkspace'), ExtHostWindow: createExtId('ExtHostWindow'), ExtHostWebviews: createExtId('ExtHostWebviews'), + ExtHostEditorInsets: createExtId('ExtHostEditorInsets'), ExtHostProgress: createMainId('ExtHostProgress'), ExtHostComments: createMainId('ExtHostComments'), ExtHostStorage: createMainId('ExtHostStorage'), diff --git a/src/vs/workbench/api/common/extHostCodeInsets.ts b/src/vs/workbench/api/common/extHostCodeInsets.ts new file mode 100644 index 0000000000000..c2a6e2de29864 --- /dev/null +++ b/src/vs/workbench/api/common/extHostCodeInsets.ts @@ -0,0 +1,130 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter } from 'vs/base/common/event'; +import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; +import * as vscode from 'vscode'; +import { MainThreadEditorInsetsShape } from './extHost.protocol'; +import { ExtHostEditors } from 'vs/workbench/api/common/extHostTextEditors'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ExtHostTextEditor } from 'vs/workbench/api/common/extHostTextEditor'; + +export class ExtHostEditorInsets implements ExtHostEditorInsets { + + private _handlePool = 0; + private _disposables = new DisposableStore(); + private _insets = new Map }>(); + + constructor( + private readonly _proxy: MainThreadEditorInsetsShape, + private readonly _editors: ExtHostEditors + ) { + + // dispose editor inset whenever the hosting editor goes away + this._disposables.add(_editors.onDidChangeVisibleTextEditors(() => { + const visibleEditor = _editors.getVisibleTextEditors(); + this._insets.forEach(value => { + if (visibleEditor.indexOf(value.editor) < 0) { + value.inset.dispose(); // will remove from `this._insets` + } + }); + })); + } + + dispose(): void { + this._insets.forEach(value => value.inset.dispose()); + this._disposables.dispose(); + } + + createWebviewEditorInset(editor: vscode.TextEditor, range: vscode.Range, options?: vscode.WebviewOptions): vscode.WebviewEditorInset { + + let apiEditor: ExtHostTextEditor | undefined; + for (const candidate of this._editors.getVisibleTextEditors()) { + if (candidate === editor) { + apiEditor = candidate; + break; + } + } + if (!apiEditor) { + throw new Error('not a visible editor'); + } + + const that = this; + const handle = this._handlePool++; + const onDidReceiveMessage = new Emitter(); + const onDidDispose = new Emitter(); + + const webview = new class implements vscode.Webview { + + private _html: string = ''; + private _options: vscode.WebviewOptions; + + set options(value: vscode.WebviewOptions) { + this._options = value; + that._proxy.$setOptions(handle, value); + } + + get options(): vscode.WebviewOptions { + return this._options; + } + + set html(value: string) { + this._html = value; + that._proxy.$setHtml(handle, value); + } + + get html(): string { + return this._html; + } + + get onDidReceiveMessage(): vscode.Event { + return onDidReceiveMessage.event; + } + + postMessage(message: any): Thenable { + return that._proxy.$postMessage(handle, message); + } + }; + + const inset = new class implements vscode.WebviewEditorInset { + + readonly editor: vscode.TextEditor = editor; + readonly range: vscode.Range = range; + readonly webview: vscode.Webview = webview; + readonly onDidDispose: vscode.Event = onDidDispose.event; + + dispose(): void { + if (that._insets.has(handle)) { + that._insets.delete(handle); + that._proxy.$disposeEditorInset(handle); + onDidDispose.fire(); + + // final cleanup + onDidDispose.dispose(); + onDidReceiveMessage.dispose(); + } + } + }; + + this._proxy.$createEditorInset(handle, apiEditor.id, apiEditor.document.uri, typeConverters.Range.from(range), options || {}); + this._insets.set(handle, { editor, inset, onDidReceiveMessage }); + + return inset; + } + + $onDidDispose(handle: number): void { + const value = this._insets.get(handle); + if (value) { + value.inset.dispose(); + } + } + + $onDidReceiveMessage(handle: number, message: any): void { + const value = this._insets.get(handle); + if (value) { + value.onDidReceiveMessage.fire(message); + } + } +} diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 3f6c11fe20aed..7a6103a6b3bb5 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -15,7 +15,7 @@ import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/common/extHostCommands'; import { ExtHostDiagnostics } from 'vs/workbench/api/common/extHostDiagnostics'; import { asPromise } from 'vs/base/common/async'; -import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, WorkspaceSymbolDto, SuggestResultDto, WorkspaceSymbolsDto, CodeActionDto, ISerializedDocumentFilter, WorkspaceEditDto, ISerializedSignatureHelpProviderMetadata, LinkDto, CodeLensDto, MainThreadWebviewsShape, CodeInsetDto, SuggestDataDto, LinksListDto, ChainedCacheId } from './extHost.protocol'; +import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, WorkspaceSymbolDto, SuggestResultDto, WorkspaceSymbolsDto, CodeActionDto, ISerializedDocumentFilter, WorkspaceEditDto, ISerializedSignatureHelpProviderMetadata, LinkDto, CodeLensDto, SuggestDataDto, LinksListDto, ChainedCacheId } 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'; @@ -25,9 +25,6 @@ import { ISelection, Selection } from 'vs/editor/common/core/selection'; import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { ExtHostWebview } from 'vs/workbench/api/common/extHostWebview'; -import * as codeInset from 'vs/workbench/contrib/codeinset/common/codeInset'; -import { generateUuid } from 'vs/base/common/uuid'; import * as callHierarchy from 'vs/workbench/contrib/callHierarchy/common/callHierarchy'; import { LRUCache } from 'vs/base/common/map'; import { IURITransformer } from 'vs/base/common/uriIpc'; @@ -152,47 +149,6 @@ class CodeLensAdapter { } } -class CodeInsetAdapter { - - constructor( - private readonly _documents: ExtHostDocuments, - private readonly _heapService: ExtHostHeapService, - private readonly _provider: vscode.CodeInsetProvider - ) { } - - provideCodeInsets(resource: URI, token: CancellationToken): Promise { - const doc = this._documents.getDocument(resource); - return asPromise(() => this._provider.provideCodeInsets(doc, token)).then(insets => { - if (Array.isArray(insets)) { - return insets.map(inset => { - const $ident = this._heapService.keep(inset); - const id = generateUuid(); - return { - $ident, - id, - range: typeConvert.Range.from(inset.range), - height: inset.height - }; - }); - } - return undefined; - }); - } - - resolveCodeInset(symbol: CodeInsetDto, webview: vscode.Webview, token: CancellationToken): Promise { - - const inset = this._heapService.get(ObjectIdentifier.of(symbol)); - if (!inset) { - return Promise.resolve(symbol); - } - - return asPromise(() => this._provider.resolveCodeInset(inset, webview, token)).then(newInset => { - newInset = newInset || inset; - return symbol; - }); - } -} - function convertToLocationLinks(value: vscode.Definition): modes.LocationLink[] { return value ? asArray(value).map(typeConvert.DefinitionLink.from) : []; } @@ -1040,7 +996,7 @@ type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | Hov | DocumentHighlightAdapter | ReferenceAdapter | CodeActionAdapter | DocumentFormattingAdapter | RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter | SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter - | ColorProviderAdapter | FoldingProviderAdapter | CodeInsetAdapter | DeclarationAdapter | SelectionRangeAdapter | CallHierarchyAdapter; + | ColorProviderAdapter | FoldingProviderAdapter | DeclarationAdapter | SelectionRangeAdapter | CallHierarchyAdapter; class AdapterData { constructor( @@ -1061,7 +1017,6 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { private _diagnostics: ExtHostDiagnostics; private _adapter = new Map(); private readonly _logService: ILogService; - private _webviewProxy: MainThreadWebviewsShape; constructor( mainContext: IMainContext, @@ -1079,7 +1034,6 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { this._heapService = heapMonitor; this._diagnostics = diagnostics; this._logService = logService; - this._webviewProxy = mainContext.getProxy(MainContext.MainThreadWebviews); } private _transformDocumentSelector(selector: vscode.DocumentSelector): Array { @@ -1202,37 +1156,6 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.resolveCodeLens(symbol, token), undefined); } - // --- code insets - - registerCodeInsetProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CodeInsetProvider): vscode.Disposable { - const handle = this._nextHandle(); - const eventHandle = typeof provider.onDidChangeCodeInsets === 'function' ? this._nextHandle() : undefined; - - this._adapter.set(handle, new AdapterData(new CodeInsetAdapter(this._documents, this._heapService, provider), extension)); - this._proxy.$registerCodeInsetSupport(handle, this._transformDocumentSelector(selector), eventHandle); - let result = this._createDisposable(handle); - - if (eventHandle !== undefined && provider.onDidChangeCodeInsets) { - const subscription = provider.onDidChangeCodeInsets(_ => this._proxy.$emitCodeLensEvent(eventHandle)); - result = Disposable.from(result, subscription); - } - - return result; - } - - $provideCodeInsets(handle: number, resource: UriComponents, token: CancellationToken): Promise { - return this._withAdapter(handle, CodeInsetAdapter, adapter => adapter.provideCodeInsets(URI.revive(resource), token), undefined); - } - - $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 ? extension.identifier : undefined, extension ? extension.extensionLocation : undefined); - return adapter.resolveCodeInset(symbol, webview, token); - }, symbol); - } - // --- declaration registerDefinitionProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable { diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index bddd0721dd0f3..b6ad9c09ee26a 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri'; import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; import * as vscode from 'vscode'; -import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewState, WebviewInsetHandle } from './extHost.protocol'; +import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewState } from './extHost.protocol'; import { Disposable } from './extHostTypes'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import * as modes from 'vs/editor/common/modes'; @@ -16,7 +16,7 @@ import * as modes from 'vs/editor/common/modes'; type IconPath = URI | { light: URI, dark: URI }; export class ExtHostWebview implements vscode.Webview { - private readonly _handle: WebviewPanelHandle | WebviewInsetHandle; + private readonly _handle: WebviewPanelHandle; private readonly _proxy: MainThreadWebviewsShape; private _html: string; private _options: vscode.WebviewOptions; @@ -26,7 +26,7 @@ export class ExtHostWebview implements vscode.Webview { public readonly onDidReceiveMessage: Event = this._onMessageEmitter.event; constructor( - handle: WebviewPanelHandle | WebviewInsetHandle, + handle: WebviewPanelHandle, proxy: MainThreadWebviewsShape, options: vscode.WebviewOptions ) { diff --git a/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts b/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts deleted file mode 100644 index ff53f793d50c8..0000000000000 --- a/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts +++ /dev/null @@ -1,7 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import '../browser/extensionHost.contribution.common'; -import './mainThreadWebview'; diff --git a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts deleted file mode 100644 index 735814fc0e18d..0000000000000 --- a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts +++ /dev/null @@ -1,437 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import { onUnexpectedError } from 'vs/base/common/errors'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; -import * as map from 'vs/base/common/map'; -import { URI, UriComponents } from 'vs/base/common/uri'; -import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import * as modes from 'vs/editor/common/modes'; -import { localize } from 'vs/nls'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; -import product from 'vs/platform/product/node/product'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewInsetHandle, WebviewPanelHandle, WebviewPanelShowOptions } from 'vs/workbench/api/common/extHost.protocol'; -import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; -import { CodeInsetController } from 'vs/workbench/contrib/codeinset/electron-browser/codeInset.contribution'; -import { WebviewEditor } from 'vs/workbench/contrib/webview/browser/webviewEditor'; -import { WebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; -import { ICreateWebViewShowOptions, IWebviewEditorService, WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewEditorService'; -import { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/webviewElement'; -import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { ACTIVE_GROUP, IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { extHostNamedCustomer } from '../common/extHostCustomers'; - -@extHostNamedCustomer(MainContext.MainThreadWebviews) -export class MainThreadWebviews extends Disposable implements MainThreadWebviewsShape { - - private static readonly standardSupportedLinkSchemes = new Set([ - 'http', - 'https', - 'mailto', - product.urlProtocol, - 'vscode', - 'vscode-insiders' - ]); - - private static revivalPool = 0; - - - private readonly _proxy: ExtHostWebviewsShape; - private readonly _webviews = new Map(); - private readonly _webviewsElements = new Map(); - private readonly _revivers = new Map(); - - private _activeWebview: WebviewPanelHandle | undefined = undefined; - - constructor( - context: IExtHostContext, - @ILifecycleService lifecycleService: ILifecycleService, - @IExtensionService extensionService: IExtensionService, - @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, - @IEditorService private readonly _editorService: IEditorService, - @IWebviewEditorService private readonly _webviewService: IWebviewEditorService, - @IOpenerService private readonly _openerService: IOpenerService, - @ITelemetryService private readonly _telemetryService: ITelemetryService, - @IInstantiationService private readonly _instantiationService: IInstantiationService, - @ICodeEditorService private readonly _codeEditorService: ICodeEditorService, - ) { - super(); - - this._proxy = context.getProxy(ExtHostContext.ExtHostWebviews); - this._register(_editorService.onDidActiveEditorChange(this.onActiveEditorChanged, this)); - this._register(_editorService.onDidVisibleEditorsChange(this.onVisibleEditorsChanged, this)); - - // This reviver's only job is to activate webview extensions - // This should trigger the real reviver to be registered from the extension host side. - this._register(_webviewService.registerReviver({ - canRevive: (webview) => { - const viewType = webview.state.viewType; - if (viewType) { - extensionService.activateByEvent(`onWebviewPanel:${viewType}`); - } - return false; - }, - reviveWebview: () => { throw new Error('not implemented'); } - })); - - this._register(lifecycleService.onBeforeShutdown(e => { - e.veto(this._onBeforeShutdown()); - }, this)); - } - - public $createWebviewPanel( - handle: WebviewPanelHandle, - viewType: string, - title: string, - showOptions: { viewColumn?: EditorViewColumn, preserveFocus?: boolean }, - options: WebviewInputOptions, - extensionId: ExtensionIdentifier, - extensionLocation: UriComponents - ): void { - const mainThreadShowOptions: ICreateWebViewShowOptions = Object.create(null); - if (showOptions) { - mainThreadShowOptions.preserveFocus = !!showOptions.preserveFocus; - mainThreadShowOptions.group = viewColumnToEditorGroup(this._editorGroupService, showOptions.viewColumn); - } - - const webview = this._webviewService.createWebview(this.getInternalWebviewId(viewType), title, mainThreadShowOptions, reviveWebviewOptions(options), { - location: URI.revive(extensionLocation), - id: extensionId - }, this.createWebviewEventDelegate(handle)); - webview.state = { - viewType: viewType, - state: undefined - }; - - this._webviews.set(handle, webview); - - /* __GDPR__ - "webviews:createWebviewPanel" : { - "extensionId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this._telemetryService.publicLog('webviews:createWebviewPanel', { extensionId: extensionId.value }); - } - - $createWebviewCodeInset( - handle: WebviewInsetHandle, - symbolId: string, - options: modes.IWebviewOptions, - extensionId: ExtensionIdentifier, - 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, - { - extension: { - location: URI.revive(extensionLocation), - id: extensionId - }, - enableFindWidget: false, - }, - { - 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; - } - } - - 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 { - const webview = this.getWebview(handle); - webview.dispose(); - } - - public $setTitle(handle: WebviewPanelHandle, value: string): void { - const webview = this.getWebview(handle); - webview.setName(value); - } - - public $setIconPath(handle: WebviewPanelHandle, value: { light: UriComponents, dark: UriComponents } | undefined): void { - const webview = this.getWebview(handle); - webview.iconPath = reviveWebviewIcon(value); - } - - public $setHtml(handle: WebviewPanelHandle | WebviewInsetHandle, value: string): void { - if (typeof handle === 'number') { - this.getWebviewElement(handle).html = value; - } else { - const webview = this.getWebview(handle); - webview.html = value; - } - } - - public $setOptions(handle: WebviewPanelHandle | WebviewInsetHandle, options: modes.IWebviewOptions): void { - if (typeof handle === 'number') { - this.getWebviewElement(handle).options = reviveWebviewOptions(options as any /*todo@mat */); - } else { - const webview = this.getWebview(handle); - webview.setOptions(reviveWebviewOptions(options as any /*todo@mat */)); - } - } - - public $reveal(handle: WebviewPanelHandle, showOptions: WebviewPanelShowOptions): void { - const webview = this.getWebview(handle); - if (webview.isDisposed()) { - return; - } - - const targetGroup = this._editorGroupService.getGroup(viewColumnToEditorGroup(this._editorGroupService, showOptions.viewColumn)) || this._editorGroupService.getGroup(webview.group || 0); - if (targetGroup) { - this._webviewService.revealWebview(webview, targetGroup, !!showOptions.preserveFocus); - } - } - - public async $postMessage(handle: WebviewPanelHandle | WebviewInsetHandle, message: any): Promise { - if (typeof handle === 'number') { - this.getWebviewElement(handle).sendMessage(message); - return true; - } 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)); - - if (editors.length > 0) { - editors[0].sendMessage(message); - return true; - } - - if (webview.webview) { - webview.webview.sendMessage(message); - return true; - } - - return false; - } - } - - public $registerSerializer(viewType: string): void { - if (this._revivers.has(viewType)) { - throw new Error(`Reviver for ${viewType} already registered`); - } - - this._revivers.set(viewType, this._webviewService.registerReviver({ - canRevive: (webview) => { - return webview.state && webview.state.viewType === viewType; - }, - reviveWebview: async (webview): Promise => { - const viewType = webview.state.viewType; - const handle = 'revival-' + MainThreadWebviews.revivalPool++; - this._webviews.set(handle, webview); - webview._events = this.createWebviewEventDelegate(handle); - let state = undefined; - if (webview.state.state) { - try { - state = JSON.parse(webview.state.state); - } catch { - // noop - } - } - - try { - await this._proxy.$deserializeWebviewPanel(handle, viewType, webview.getTitle(), state, editorGroupToViewColumn(this._editorGroupService, webview.group || 0), webview.options); - } catch (error) { - onUnexpectedError(error); - webview.html = MainThreadWebviews.getDeserializationFailedContents(viewType); - } - } - })); - } - - public $unregisterSerializer(viewType: string): void { - const reviver = this._revivers.get(viewType); - if (!reviver) { - throw new Error(`No reviver for ${viewType} registered`); - } - - reviver.dispose(); - this._revivers.delete(viewType); - } - - private getInternalWebviewId(viewType: string): string { - return `mainThreadWebview-${viewType}`; - } - - private _onBeforeShutdown(): boolean { - this._webviews.forEach((webview) => { - if (!webview.isDisposed() && webview.state && this._revivers.has(webview.state.viewType)) { - webview.state.state = webview.webviewState; - } - }); - return false; // Don't veto shutdown - } - - private createWebviewEventDelegate(handle: WebviewPanelHandle) { - return { - onDidClickLink: (uri: URI) => this.onDidClickLink(handle, uri), - onMessage: (message: any) => this._proxy.$onMessage(handle, message), - onDispose: () => { - this._proxy.$onDidDisposeWebviewPanel(handle).finally(() => { - this._webviews.delete(handle); - }); - } - }; - } - - private onActiveEditorChanged() { - const activeEditor = this._editorService.activeControl; - let newActiveWebview: { input: WebviewEditorInput, handle: WebviewPanelHandle } | undefined = undefined; - if (activeEditor && activeEditor.input instanceof WebviewEditorInput) { - for (const handle of map.keys(this._webviews)) { - const input = this._webviews.get(handle)!; - if (input.matches(activeEditor.input)) { - newActiveWebview = { input, handle }; - break; - } - } - } - - if (newActiveWebview && newActiveWebview.handle === this._activeWebview) { - // Webview itself unchanged but position may have changed - this._proxy.$onDidChangeWebviewPanelViewState(newActiveWebview.handle, { - active: true, - visible: true, - position: editorGroupToViewColumn(this._editorGroupService, newActiveWebview.input.group || 0) - }); - return; - } - - // Broadcast view state update for currently active - if (typeof this._activeWebview !== 'undefined') { - const oldActiveWebview = this._webviews.get(this._activeWebview); - if (oldActiveWebview) { - this._proxy.$onDidChangeWebviewPanelViewState(this._activeWebview, { - active: false, - visible: this._editorService.visibleControls.some(editor => !!editor.input && editor.input.matches(oldActiveWebview)), - position: editorGroupToViewColumn(this._editorGroupService, oldActiveWebview.group || 0), - }); - } - } - - // Then for newly active - if (newActiveWebview) { - this._proxy.$onDidChangeWebviewPanelViewState(newActiveWebview.handle, { - active: true, - visible: true, - position: editorGroupToViewColumn(this._editorGroupService, activeEditor ? activeEditor.group : ACTIVE_GROUP), - }); - this._activeWebview = newActiveWebview.handle; - } else { - this._activeWebview = undefined; - } - } - - private onVisibleEditorsChanged(): void { - this._webviews.forEach((input, handle) => { - for (const workbenchEditor of this._editorService.visibleControls) { - if (workbenchEditor.input && workbenchEditor.input.matches(input)) { - const editorPosition = editorGroupToViewColumn(this._editorGroupService, workbenchEditor.group!); - - input.updateGroup(workbenchEditor.group!.id); - this._proxy.$onDidChangeWebviewPanelViewState(handle, { - active: handle === this._activeWebview, - visible: true, - position: editorPosition - }); - break; - } - } - }); - } - - private onDidClickLink(handle: WebviewPanelHandle, link: URI): void { - if (!link) { - return; - } - - const webview = this.getWebview(handle); - if (this.isSupportedLink(webview, link)) { - this._openerService.open(link); - } - } - - private isSupportedLink(webview: WebviewEditorInput, link: URI): boolean { - if (MainThreadWebviews.standardSupportedLinkSchemes.has(link.scheme)) { - return true; - } - return !!webview.options.enableCommandUris && link.scheme === 'command'; - } - - private getWebview(handle: WebviewPanelHandle): WebviewEditorInput { - const webview = this._webviews.get(handle); - if (!webview) { - throw new Error('Unknown webview handle:' + handle); - } - return webview; - } - - private getWebviewElement(handle: number): WebviewElement { - const webview = this._webviewsElements.get(handle); - if (!webview) { - throw new Error('Unknown webview handle:' + handle); - } - return webview; - } - - private static getDeserializationFailedContents(viewType: string) { - return ` - - - - - - - ${localize('errorMessage', "An error occurred while restoring view:{0}", viewType)} - `; - } -} - -function reviveWebviewOptions(options: WebviewInputOptions): WebviewInputOptions { - return { - ...options, - localResourceRoots: Array.isArray(options.localResourceRoots) ? options.localResourceRoots.map(r => URI.revive(r)) : undefined, - }; -} - -function reviveWebviewIcon( - value: { light: UriComponents, dark: UriComponents } | undefined -): { light: URI, dark: URI } | undefined { - if (!value) { - return undefined; - } - - return { - light: URI.revive(value.light), - dark: URI.revive(value.dark) - }; -} diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 5e79d7bed44d9..64e63796191f8 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -67,6 +67,7 @@ import { withNullAsUndefined } from 'vs/base/common/types'; import { values } from 'vs/base/common/collections'; import { Schemas } from 'vs/base/common/network'; import { IURITransformer } from 'vs/base/common/uriIpc'; +import { ExtHostEditorInsets } from 'vs/workbench/api/common/extHostCodeInsets'; export interface IExtensionApiFactory { (extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode; @@ -109,6 +110,7 @@ export function createApiFactory( const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands, extHostLogService)); rpcProtocol.set(ExtHostContext.ExtHostWorkspace, extHostWorkspace); rpcProtocol.set(ExtHostContext.ExtHostConfiguration, extHostConfiguration); + const extHostEditorInsets = rpcProtocol.set(ExtHostContext.ExtHostEditorInsets, new ExtHostEditorInsets(rpcProtocol.getProxy(MainContext.MainThreadEditorInsets), extHostEditors)); const extHostDiagnostics = rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(rpcProtocol)); const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, uriTransformer, extHostDocuments, extHostCommands, extHostHeapService, extHostDiagnostics, extHostLogService)); const extHostFileSystem = rpcProtocol.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(rpcProtocol, extHostLanguageFeatures)); @@ -306,10 +308,6 @@ export function createApiFactory( registerCodeLensProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable { return extHostLanguageFeatures.registerCodeLensProvider(extension, checkSelector(selector), provider); }, - registerCodeInsetProvider(selector: vscode.DocumentSelector, provider: vscode.CodeInsetProvider): vscode.Disposable { - checkProposedApiEnabled(extension); - return extHostLanguageFeatures.registerCodeInsetProvider(extension, checkSelector(selector), provider); - }, registerDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable { return extHostLanguageFeatures.registerDefinitionProvider(extension, checkSelector(selector), provider); }, @@ -486,6 +484,10 @@ export function createApiFactory( createWebviewPanel(viewType: string, title: string, showOptions: vscode.ViewColumn | { viewColumn: vscode.ViewColumn, preserveFocus?: boolean }, options: vscode.WebviewPanelOptions & vscode.WebviewOptions): vscode.WebviewPanel { return extHostWebviews.createWebviewPanel(extension, viewType, title, showOptions, options); }, + createWebviewTextEditorInset(editor: vscode.TextEditor, range: vscode.Range, options: vscode.WebviewOptions): vscode.WebviewEditorInset { + checkProposedApiEnabled(extension); + return extHostEditorInsets.createWebviewEditorInset(editor, range, options); + }, createTerminal(nameOrOptions?: vscode.TerminalOptions | string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal { if (typeof nameOrOptions === 'object') { nameOrOptions.runInBackground = nameOrOptions.runInBackground && extension.enableProposedApi; diff --git a/src/vs/workbench/contrib/codeinset/common/codeInset.ts b/src/vs/workbench/contrib/codeinset/common/codeInset.ts deleted file mode 100644 index cb37c59d8f9d6..0000000000000 --- a/src/vs/workbench/contrib/codeinset/common/codeInset.ts +++ /dev/null @@ -1,70 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { ITextModel } from 'vs/editor/common/model'; -import { onUnexpectedExternalError } from 'vs/base/common/errors'; -import { mergeSort } from 'vs/base/common/arrays'; -import { Event } from 'vs/base/common/event'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { LanguageFeatureRegistry } from 'vs/editor/common/modes/languageFeatureRegistry'; -import { ProviderResult } from 'vs/editor/common/modes'; -import { IRange } from 'vs/editor/common/core/range'; - -export interface ICodeInsetSymbol { - id: string; - range: IRange; - height?: number; -} - -export interface CodeInsetProvider { - onDidChange?: Event; - provideCodeInsets(model: ITextModel, token: CancellationToken): ProviderResult; - resolveCodeInset(model: ITextModel, codeInset: ICodeInsetSymbol, token: CancellationToken): ProviderResult; -} - -export const CodeInsetProviderRegistry = new LanguageFeatureRegistry(); - -export interface ICodeInsetData { - symbol: ICodeInsetSymbol; - provider: CodeInsetProvider; - resolved?: boolean; -} - -export function getCodeInsetData(model: ITextModel, token: CancellationToken): Promise { - - const symbols: ICodeInsetData[] = []; - const providers = CodeInsetProviderRegistry.ordered(model); - - const promises = providers.map(provider => - Promise.resolve(provider.provideCodeInsets(model, token)).then(result => { - if (Array.isArray(result)) { - for (let symbol of result) { - symbols.push({ symbol, provider }); - } - } - }).catch(onUnexpectedExternalError)); - - return Promise.all(promises).then(() => { - - return mergeSort(symbols, (a, b) => { - // sort by lineNumber, provider-rank, and column - if (a.symbol.range.startLineNumber < b.symbol.range.startLineNumber) { - return -1; - } else if (a.symbol.range.startLineNumber > b.symbol.range.startLineNumber) { - return 1; - } else if (providers.indexOf(a.provider) < providers.indexOf(b.provider)) { - return -1; - } else if (providers.indexOf(a.provider) > providers.indexOf(b.provider)) { - return 1; - } else if (a.symbol.range.startColumn < b.symbol.range.startColumn) { - return -1; - } else if (a.symbol.range.startColumn > b.symbol.range.startColumn) { - return 1; - } else { - return 0; - } - }); - }); -} diff --git a/src/vs/workbench/contrib/codeinset/electron-browser/codeInset.contribution.ts b/src/vs/workbench/contrib/codeinset/electron-browser/codeInset.contribution.ts deleted file mode 100644 index 8a8ef48b271a2..0000000000000 --- a/src/vs/workbench/contrib/codeinset/electron-browser/codeInset.contribution.ts +++ /dev/null @@ -1,353 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { CancelablePromise, createCancelablePromise, RunOnceScheduler } from 'vs/base/common/async'; -import { onUnexpectedError } from 'vs/base/common/errors'; -import { dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { StableEditorScrollState } from 'vs/editor/browser/core/editorState'; -import * as editorBrowser from 'vs/editor/browser/editorBrowser'; -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { IModelDecorationsChangeAccessor } from 'vs/editor/common/model'; -import { CodeInsetProviderRegistry, getCodeInsetData, ICodeInsetData } from '../common/codeInset'; -import { CodeInsetWidget, CodeInsetHelper } from './codeInsetWidget'; -import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/webviewElement'; -// import { localize } from 'vs/nls'; - -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; - - private _globalToDispose: IDisposable[]; - private _localToDispose: IDisposable[]; - private _insetWidgets: CodeInsetWidget[]; - private _pendingWebviews = new Map any>(); - private _currentFindCodeInsetSymbolsPromise: CancelablePromise | null; - private _modelChangeCounter: number; - private _currentResolveCodeInsetSymbolsPromise: CancelablePromise | null; - private _detectVisibleInsets: RunOnceScheduler; - - constructor( - private _editor: editorBrowser.ICodeEditor, - @IConfigurationService private readonly _configService: IConfigurationService, - ) { - this._isEnabled = this._configService.getValue('editor.codeInsets'); - - this._globalToDispose = []; - this._localToDispose = []; - this._insetWidgets = []; - this._currentFindCodeInsetSymbolsPromise = null; - this._modelChangeCounter = 0; - - this._globalToDispose.push(this._editor.onDidChangeModel(() => this._onModelChange())); - this._globalToDispose.push(this._editor.onDidChangeModelLanguage(() => this._onModelChange())); - this._globalToDispose.push(this._configService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('editor.codeInsets')) { - let prevIsEnabled = this._isEnabled; - this._isEnabled = this._configService.getValue('editor.codeInsets'); - if (prevIsEnabled !== this._isEnabled) { - this._onModelChange(); - } - } - })); - this._globalToDispose.push(CodeInsetProviderRegistry.onDidChange(this._onModelChange, this)); - this._onModelChange(); - } - - dispose(): void { - this._localDispose(); - this._globalToDispose = dispose(this._globalToDispose); - } - - acceptWebview(symbolId: string, webviewElement: WebviewElement): boolean { - const pendingWebview = this._pendingWebviews.get(symbolId); - if (pendingWebview) { - pendingWebview(webviewElement); - this._pendingWebviews.delete(symbolId); - return true; - } - return false; - } - - private _localDispose(): void { - if (this._currentFindCodeInsetSymbolsPromise) { - this._currentFindCodeInsetSymbolsPromise.cancel(); - this._currentFindCodeInsetSymbolsPromise = null; - this._modelChangeCounter++; - } - if (this._currentResolveCodeInsetSymbolsPromise) { - this._currentResolveCodeInsetSymbolsPromise.cancel(); - this._currentResolveCodeInsetSymbolsPromise = null; - } - this._localToDispose = dispose(this._localToDispose); - } - - getId(): string { - return CodeInsetController.ID; - } - - private _onModelChange(): void { - this._localDispose(); - - const model = this._editor.getModel(); - if (!model || !this._isEnabled || !CodeInsetProviderRegistry.has(model)) { - return; - } - - for (const provider of CodeInsetProviderRegistry.all(model)) { - if (typeof provider.onDidChange === 'function') { - let registration = provider.onDidChange(() => scheduler.schedule()); - this._localToDispose.push(registration); - } - } - - this._detectVisibleInsets = new RunOnceScheduler(() => { - this._onViewportChanged(); - }, 500); - - const scheduler = new RunOnceScheduler(() => { - const counterValue = ++this._modelChangeCounter; - if (this._currentFindCodeInsetSymbolsPromise) { - this._currentFindCodeInsetSymbolsPromise.cancel(); - } - - this._currentFindCodeInsetSymbolsPromise = createCancelablePromise(token => getCodeInsetData(model, token)); - - this._currentFindCodeInsetSymbolsPromise.then(codeInsetData => { - if (counterValue === this._modelChangeCounter) { // only the last one wins - this._renderCodeInsetSymbols(codeInsetData); - this._detectVisibleInsets.schedule(); - } - }, onUnexpectedError); - }, 250); - - this._localToDispose.push(scheduler); - - this._localToDispose.push(this._detectVisibleInsets); - - this._localToDispose.push(this._editor.onDidChangeModelContent(() => { - this._editor.changeDecorations(changeAccessor => { - this._editor.changeViewZones(viewAccessor => { - let toDispose: CodeInsetWidget[] = []; - let lastInsetLineNumber: number = -1; - this._insetWidgets.forEach(inset => { - if (!inset.isValid() || lastInsetLineNumber === inset.getLineNumber()) { - // invalid -> Inset collapsed, attach range doesn't exist anymore - // line_number -> insets should never be on the same line - toDispose.push(inset); - } - else { - inset.reposition(viewAccessor); - lastInsetLineNumber = inset.getLineNumber(); - } - }); - let helper = new CodeInsetHelper(); - toDispose.forEach((l) => { - l.dispose(helper, viewAccessor); - this._insetWidgets.splice(this._insetWidgets.indexOf(l), 1); - }); - helper.commit(changeAccessor); - }); - }); - // Compute new `visible` code insets - this._detectVisibleInsets.schedule(); - // Ask for all references again - scheduler.schedule(); - })); - - this._localToDispose.push(this._editor.onDidScrollChange(e => { - if (e.scrollTopChanged && this._insetWidgets.length > 0) { - this._detectVisibleInsets.schedule(); - } - })); - - this._localToDispose.push(this._editor.onDidLayoutChange(() => { - this._detectVisibleInsets.schedule(); - })); - - this._localToDispose.push(toDisposable(() => { - if (this._editor.getModel()) { - const scrollState = StableEditorScrollState.capture(this._editor); - this._editor.changeDecorations((changeAccessor) => { - this._editor.changeViewZones((accessor) => { - this._disposeAllInsets(changeAccessor, accessor); - }); - }); - scrollState.restore(this._editor); - } else { - // No accessors available - this._disposeAllInsets(null, null); - } - })); - - scheduler.schedule(); - } - - private _disposeAllInsets(decChangeAccessor: IModelDecorationsChangeAccessor | null, viewZoneChangeAccessor: editorBrowser.IViewZoneChangeAccessor | null): void { - let helper = new CodeInsetHelper(); - this._insetWidgets.forEach((Inset) => Inset.dispose(helper, viewZoneChangeAccessor)); - if (decChangeAccessor) { - helper.commit(decChangeAccessor); - } - this._insetWidgets = []; - } - - private _renderCodeInsetSymbols(symbols: ICodeInsetData[]): void { - if (!this._editor.hasModel()) { - return; - } - - let maxLineNumber = this._editor.getModel().getLineCount(); - let groups: ICodeInsetData[][] = []; - let lastGroup: ICodeInsetData[] | undefined; - - for (let symbol of symbols) { - let line = symbol.symbol.range.startLineNumber; - if (line < 1 || line > maxLineNumber) { - // invalid code Inset - continue; - } else if (lastGroup && lastGroup[lastGroup.length - 1].symbol.range.startLineNumber === line) { - // on same line as previous - lastGroup.push(symbol); - } else { - // on later line as previous - lastGroup = [symbol]; - groups.push(lastGroup); - } - } - - const scrollState = StableEditorScrollState.capture(this._editor); - - this._editor.changeDecorations(changeAccessor => { - this._editor.changeViewZones(accessor => { - - let codeInsetIndex = 0, groupsIndex = 0, helper = new CodeInsetHelper(); - - while (groupsIndex < groups.length && codeInsetIndex < this._insetWidgets.length) { - - let symbolsLineNumber = groups[groupsIndex][0].symbol.range.startLineNumber; - let codeInsetLineNumber = this._insetWidgets[codeInsetIndex].getLineNumber(); - - if (codeInsetLineNumber < symbolsLineNumber) { - this._insetWidgets[codeInsetIndex].dispose(helper, accessor); - this._insetWidgets.splice(codeInsetIndex, 1); - } else if (codeInsetLineNumber === symbolsLineNumber) { - this._insetWidgets[codeInsetIndex].updateCodeInsetSymbols(groups[groupsIndex], helper); - groupsIndex++; - codeInsetIndex++; - } else { - this._insetWidgets.splice( - codeInsetIndex, - 0, - new CodeInsetWidget(groups[groupsIndex], this._editor, helper) - ); - codeInsetIndex++; - groupsIndex++; - } - } - - // Delete extra code insets - while (codeInsetIndex < this._insetWidgets.length) { - this._insetWidgets[codeInsetIndex].dispose(helper, accessor); - this._insetWidgets.splice(codeInsetIndex, 1); - } - - // Create extra symbols - while (groupsIndex < groups.length) { - this._insetWidgets.push(new CodeInsetWidget( - groups[groupsIndex], - this._editor, helper - )); - groupsIndex++; - } - - helper.commit(changeAccessor); - }); - }); - - scrollState.restore(this._editor); - } - - private _onViewportChanged(): void { - if (this._currentResolveCodeInsetSymbolsPromise) { - this._currentResolveCodeInsetSymbolsPromise.cancel(); - this._currentResolveCodeInsetSymbolsPromise = null; - } - - const model = this._editor.getModel(); - if (!model) { - return; - } - - const allWidgetRequests: ICodeInsetData[][] = []; - const insetWidgets: CodeInsetWidget[] = []; - this._insetWidgets.forEach(inset => { - const widgetRequests = inset.computeIfNecessary(model); - if (widgetRequests) { - allWidgetRequests.push(widgetRequests); - insetWidgets.push(inset); - } - }); - - if (allWidgetRequests.length === 0) { - return; - } - - this._currentResolveCodeInsetSymbolsPromise = createCancelablePromise(token => { - - const allPromises = allWidgetRequests.map((widgetRequests, r) => { - - const widgetPromises = widgetRequests.map(request => { - if (request.resolved) { - return Promise.resolve(undefined); - } - 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); - }); - - return Promise.all(allPromises); - }); - - this._currentResolveCodeInsetSymbolsPromise.then(() => { - this._currentResolveCodeInsetSymbolsPromise = null; - }).catch(err => { - this._currentResolveCodeInsetSymbolsPromise = null; - onUnexpectedError(err); - }); - } -} - -registerEditorContribution(CodeInsetController); - - -Registry.as(ConfigurationExtensions.Configuration).registerConfiguration({ - id: 'editor', - properties: { - // ['editor.codeInsets']: { - // description: localize('editor.codeInsets', "Enable/disable editor code insets"), - // type: 'boolean', - // default: false - // } - } -}); diff --git a/src/vs/workbench/contrib/codeinset/electron-browser/codeInsetWidget.css b/src/vs/workbench/contrib/codeinset/electron-browser/codeInsetWidget.css deleted file mode 100644 index 2804246129860..0000000000000 --- a/src/vs/workbench/contrib/codeinset/electron-browser/codeInsetWidget.css +++ /dev/null @@ -1,8 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -.monaco-editor .code-inset { - z-index: 10; -} diff --git a/src/vs/workbench/contrib/codeinset/electron-browser/codeInsetWidget.ts b/src/vs/workbench/contrib/codeinset/electron-browser/codeInsetWidget.ts deleted file mode 100644 index d5e16dc47a9bd..0000000000000 --- a/src/vs/workbench/contrib/codeinset/electron-browser/codeInsetWidget.ts +++ /dev/null @@ -1,193 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import 'vs/css!./codeInsetWidget'; -import { Range } from 'vs/editor/common/core/range'; -import * as editorBrowser from 'vs/editor/browser/editorBrowser'; -import { ICodeInsetData } from '../common/codeInset'; -import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; -import { IModelDeltaDecoration, IModelDecorationsChangeAccessor, ITextModel } from 'vs/editor/common/model'; -import { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/webviewElement'; - - -export interface IDecorationIdCallback { - (decorationId: string): void; -} - -export class CodeInsetHelper { - - private _removeDecorations: string[]; - private _addDecorations: IModelDeltaDecoration[]; - private _addDecorationsCallbacks: IDecorationIdCallback[]; - - constructor() { - this._removeDecorations = []; - this._addDecorations = []; - this._addDecorationsCallbacks = []; - } - - addDecoration(decoration: IModelDeltaDecoration, callback: IDecorationIdCallback): void { - this._addDecorations.push(decoration); - this._addDecorationsCallbacks.push(callback); - } - - removeDecoration(decorationId: string): void { - this._removeDecorations.push(decorationId); - } - - commit(changeAccessor: IModelDecorationsChangeAccessor): void { - let resultingDecorations = changeAccessor.deltaDecorations(this._removeDecorations, this._addDecorations); - for (let i = 0, len = resultingDecorations.length; i < len; i++) { - this._addDecorationsCallbacks[i](resultingDecorations[i]); - } - } -} - -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 _range: Range; - - constructor( - data: ICodeInsetData[], // all the insets on the same line (often just one) - editor: editorBrowser.ICodeEditor, - helper: CodeInsetHelper - ) { - this._editor = editor; - this._data = data; - this._decorationIds = new Array(this._data.length); - - this._data.forEach((codeInsetData, i) => { - - helper.addDecoration({ - range: codeInsetData.symbol.range, - options: ModelDecorationOptions.EMPTY - }, id => this._decorationIds[i] = id); - - // the range contains all insets on this line - if (!this._range) { - this._range = Range.lift(codeInsetData.symbol.range); - } else { - this._range = Range.plusRange(this._range, codeInsetData.symbol.range); - } - }); - } - - public dispose(helper: CodeInsetHelper, viewZoneChangeAccessor: editorBrowser.IViewZoneChangeAccessor | null): void { - while (this._decorationIds.length) { - const decoration = this._decorationIds.pop(); - if (decoration) { - helper.removeDecoration(decoration); - } - } - if (viewZoneChangeAccessor) { - if (typeof this._viewZoneId !== 'undefined') { - viewZoneChangeAccessor.removeZone(this._viewZoneId); - } - this._viewZone = undefined!; - } - if (this._webview) { - this._webview.dispose(); - } - } - - public isValid(): boolean { - return this._editor.hasModel() && this._decorationIds.some((id, i) => { - const range = this._editor.getModel()!.getDecorationRange(id); - const symbol = this._data[i].symbol; - return !!range && Range.isEmpty(symbol.range) === range.isEmpty(); - }); - } - - public updateCodeInsetSymbols(data: ICodeInsetData[], helper: CodeInsetHelper): void { - while (this._decorationIds.length) { - const decoration = this._decorationIds.pop(); - if (decoration) { - helper.removeDecoration(decoration); - } - } - this._data = data; - this._decorationIds = new Array(this._data.length); - this._data.forEach((codeInsetData, i) => { - helper.addDecoration({ - range: codeInsetData.symbol.range, - options: ModelDecorationOptions.EMPTY - }, id => this._decorationIds[i] = id); - }); - } - - public computeIfNecessary(model: ITextModel): ICodeInsetData[] { - // Read editor current state - for (let i = 0; i < this._decorationIds.length; i++) { - const range = model.getDecorationRange(this._decorationIds[i]); - if (range) { - this._data[i].symbol.range = range; - } - } - return this._data; - } - - public getLineNumber(): number { - if (this._editor.hasModel()) { - const range = this._editor.getModel().getDecorationRange(this._decorationIds[0]); - if (range) { - return range.startLineNumber; - } - } - return -1; - } - - public adoptWebview(webview: WebviewElement): void { - - const lineNumber = this._range.endLineNumber; - this._editor.changeViewZones(accessor => { - - if (this._viewZoneId) { - accessor.removeZone(this._viewZoneId); - this._webview.dispose(); - } - - const div = document.createElement('div'); - div.className = 'code-inset'; - webview.mountTo(div); - webview.onMessage((e: { type: string, payload: any }) => { - // The webview contents can use a "size-info" message to report its size. - if (e && e.type === 'size-info') { - const margin = e.payload.height > 0 ? 5 : 0; - this._viewZone.heightInPx = e.payload.height + margin; - this._editor.changeViewZones(accessor => { - if (this._viewZoneId) { - accessor.layoutZone(this._viewZoneId); - } - }); - } - }); - this._viewZone = { - afterLineNumber: lineNumber, - heightInPx: 50, - domNode: div - }; - this._viewZoneId = accessor.addZone(this._viewZone); - this._webview = webview; - }); - } - - public reposition(viewZoneChangeAccessor: editorBrowser.IViewZoneChangeAccessor): void { - if (this.isValid() && this._editor.hasModel()) { - const range = this._editor.getModel().getDecorationRange(this._decorationIds[0]); - if (range) { - this._viewZone.afterLineNumber = range.endLineNumber; - } - if (this._viewZoneId) { - viewZoneChangeAccessor.layoutZone(this._viewZoneId); - } - } - } -} diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts b/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts index 85975f66fb29d..8eb33010acb52 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts @@ -327,4 +327,4 @@ export class RevivedWebviewEditorInput extends WebviewEditorInput { } return super.resolve(); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/test/electron-browser/api/extHostWebview.test.ts b/src/vs/workbench/test/electron-browser/api/extHostWebview.test.ts index a0d8a8c41adb5..2650c602cdf7e 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostWebview.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostWebview.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { MainThreadWebviews } from 'vs/workbench/api/electron-browser/mainThreadWebview'; +import { MainThreadWebviews } from 'vs/workbench/api/browser/mainThreadWebview'; import { ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview'; import { mock } from 'vs/workbench/test/electron-browser/api/mock'; import * as vscode from 'vscode'; diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index 78cc5e9594298..9667ecad1d444 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -7,7 +7,7 @@ import 'vs/editor/editor.all'; -import 'vs/workbench/api/electron-browser/extensionHost.contribution'; +import 'vs/workbench/api/browser/extensionHost.contribution'; import 'vs/workbench/electron-browser/main.contribution'; import 'vs/workbench/browser/workbench.contribution'; @@ -324,9 +324,6 @@ import 'vs/workbench/contrib/outline/browser/outline.contribution'; // Experiments import 'vs/workbench/contrib/experiments/electron-browser/experiments.contribution'; -// Code Insets -import 'vs/workbench/contrib/codeinset/electron-browser/codeInset.contribution'; - // Issues import 'vs/workbench/contrib/issue/electron-browser/issue.contribution'; diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index 147c0818ae436..b6609ac5f9775 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -322,9 +322,6 @@ import 'vs/workbench/contrib/outline/browser/outline.contribution'; // Experiments // import 'vs/workbench/contrib/experiments/electron-browser/experiments.contribution'; -// Code Insets -// import 'vs/workbench/contrib/codeinset/electron-browser/codeInset.contribution'; - // Issues // import 'vs/workbench/contrib/issue/electron-browser/issue.contribution';