From 066dfef8f70379c9d4d8fbf3d2d4d0a2259c331e Mon Sep 17 00:00:00 2001 From: Rob DeLine Date: Tue, 12 Feb 2019 13:00:42 +0100 Subject: [PATCH 01/14] Add Code Inset Feature --- src/vs/editor/common/config/editorOptions.ts | 9 + src/vs/editor/common/modes.ts | 20 +- .../contrib/gotoError/gotoErrorWidget.ts | 2 +- .../referenceSearch/referencesWidget.ts | 4 +- src/vs/monaco.d.ts | 20 ++ src/vs/vscode.d.ts | 24 ++ .../mainThreadLanguageFeatures.ts | 33 +- .../api/electron-browser/mainThreadWebview.ts | 27 +- src/vs/workbench/api/node/extHost.api.impl.ts | 4 + src/vs/workbench/api/node/extHost.protocol.ts | 20 +- .../workbench/api/node/extHostApiCommands.ts | 15 + .../api/node/extHostLanguageFeatures.ts | 85 ++++- src/vs/workbench/api/node/extHostTypes.ts | 15 + src/vs/workbench/api/node/extHostWebview.ts | 2 +- .../browser/parts/quickinput/quickInputBox.ts | 24 +- .../electron-browser/views/explorerView.ts | 5 + .../electron-browser/dirtydiffDecorator.ts | 4 +- .../webview/electron-browser/webview-pre.js | 2 +- .../electron-browser/webviewEditorService.ts | 25 ++ .../electron-browser/webviewElement.ts | 51 ++- .../parts/codeinset/codeInset.contribution.ts | 335 ++++++++++++++++++ src/vs/workbench/parts/codeinset/codeInset.ts | 87 +++++ .../parts/codeinset/codeInsetWidget.css | 45 +++ .../parts/codeinset/codeInsetWidget.ts | 191 ++++++++++ .../services/extensions/common/extensions.ts | 3 + .../extensionHostProcessManager.ts | 6 + .../electron-browser/extensionService.ts | 5 + src/vs/workbench/workbench.main.ts | 4 + 28 files changed, 1032 insertions(+), 35 deletions(-) create mode 100644 src/vs/workbench/parts/codeinset/codeInset.contribution.ts create mode 100644 src/vs/workbench/parts/codeinset/codeInset.ts create mode 100644 src/vs/workbench/parts/codeinset/codeInsetWidget.css create mode 100644 src/vs/workbench/parts/codeinset/codeInsetWidget.ts diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index d8ee1d9226bba..ad23683521479 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -600,6 +600,11 @@ export interface IEditorOptions { * Defaults to true. */ codeLens?: boolean; + /** + * Show code insets + * Defaults to true. + */ + codeInsets?: boolean; /** * Control the behavior and rendering of the code action lightbulb. */ @@ -996,6 +1001,7 @@ export interface EditorContribOptions { readonly selectionHighlight: boolean; readonly occurrencesHighlight: boolean; readonly codeLens: boolean; + readonly codeInsets: boolean; readonly folding: boolean; readonly foldingStrategy: 'auto' | 'indentation'; readonly showFoldingControls: 'always' | 'mouseover'; @@ -2051,6 +2057,7 @@ export class EditorOptionsValidator { selectionHighlight: _boolean(opts.selectionHighlight, defaults.selectionHighlight), occurrencesHighlight: _boolean(opts.occurrencesHighlight, defaults.occurrencesHighlight), codeLens: _boolean(opts.codeLens, defaults.codeLens), + codeInsets: _boolean(opts.codeInsets, defaults.codeInsets), folding: _boolean(opts.folding, defaults.folding), foldingStrategy: _stringSet<'auto' | 'indentation'>(opts.foldingStrategy, defaults.foldingStrategy, ['auto', 'indentation']), showFoldingControls: _stringSet<'always' | 'mouseover'>(opts.showFoldingControls, defaults.showFoldingControls, ['always', 'mouseover']), @@ -2164,6 +2171,7 @@ export class InternalEditorOptionsFactory { selectionHighlight: (accessibilityIsOn ? false : opts.contribInfo.selectionHighlight), // DISABLED WHEN SCREEN READER IS ATTACHED occurrencesHighlight: (accessibilityIsOn ? false : opts.contribInfo.occurrencesHighlight), // DISABLED WHEN SCREEN READER IS ATTACHED codeLens: (accessibilityIsOn ? false : opts.contribInfo.codeLens), // DISABLED WHEN SCREEN READER IS ATTACHED + codeInsets: (accessibilityIsOn ? false : opts.contribInfo.codeInsets), folding: (accessibilityIsOn ? false : opts.contribInfo.folding), // DISABLED WHEN SCREEN READER IS ATTACHED foldingStrategy: opts.contribInfo.foldingStrategy, showFoldingControls: opts.contribInfo.showFoldingControls, @@ -2653,6 +2661,7 @@ export const EDITOR_DEFAULTS: IValidatedEditorOptions = { selectionHighlight: true, occurrencesHighlight: true, codeLens: true, + codeInsets: true, folding: true, foldingStrategy: 'auto', showFoldingControls: 'mouseover', diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 6126701924d3a..895cbc0a20603 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -9,7 +9,7 @@ import { Event } from 'vs/base/common/event'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { IDisposable } from 'vs/base/common/lifecycle'; import { isObject } from 'vs/base/common/types'; -import { URI } from 'vs/base/common/uri'; +import { URI, UriComponents } from 'vs/base/common/uri'; import { Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; @@ -1321,6 +1321,19 @@ export interface CodeLensProvider { resolveCodeLens?(model: model.ITextModel, codeLens: ICodeLensSymbol, token: CancellationToken): ProviderResult; } +export interface ICodeInsetSymbol { + range: IRange; + id?: string; + height?: number; + webviewHandle?: string; +} +export interface CodeInsetProvider { + onDidChange?: Event; + extensionLocation: UriComponents; + provideCodeInsets(model: model.ITextModel, token: CancellationToken): ProviderResult; + resolveCodeInset?(model: model.ITextModel, codeInset: ICodeInsetSymbol, token: CancellationToken): ProviderResult; +} + // --- feature registries ------ /** @@ -1383,6 +1396,11 @@ export const TypeDefinitionProviderRegistry = new LanguageFeatureRegistry(); +/** + * @internal + */ +export const CodeInsetProviderRegistry = new LanguageFeatureRegistry(); + /** * @internal */ diff --git a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts index fd3598b5a2840..74e424d84c287 100644 --- a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts +++ b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts @@ -188,7 +188,7 @@ export class MarkerNavigationWidget extends ZoneWidget { } private _applyTheme(theme: ITheme) { - this._backgroundColor = theme.getColor(editorMarkerNavigationBackground); + this._backgroundColor = theme.getColor(editorMarkerNavigationBackground) || undefined; let colorId = editorMarkerNavigationError; if (this._severity === MarkerSeverity.Warning) { colorId = editorMarkerNavigationWarning; diff --git a/src/vs/editor/contrib/referenceSearch/referencesWidget.ts b/src/vs/editor/contrib/referenceSearch/referencesWidget.ts index 4fd0bbebf594b..23068af099a49 100644 --- a/src/vs/editor/contrib/referenceSearch/referencesWidget.ts +++ b/src/vs/editor/contrib/referenceSearch/referencesWidget.ts @@ -279,8 +279,8 @@ export class ReferenceWidget extends PeekViewWidget { arrowColor: borderColor, frameColor: borderColor, headerBackgroundColor: theme.getColor(peekViewTitleBackground) || Color.transparent, - primaryHeadingColor: theme.getColor(peekViewTitleForeground), - secondaryHeadingColor: theme.getColor(peekViewTitleInfoForeground) + primaryHeadingColor: theme.getColor(peekViewTitleForeground) || undefined, + secondaryHeadingColor: theme.getColor(peekViewTitleInfoForeground) || undefined }); } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index b994864125b56..f3f4f799dc4ba 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -2934,6 +2934,11 @@ declare namespace monaco.editor { * Defaults to true. */ codeLens?: boolean; + /** + * Show code insets + * Defaults to true. + */ + codeInsets?: boolean; /** * Control the behavior and rendering of the code action lightbulb. */ @@ -3271,6 +3276,7 @@ declare namespace monaco.editor { readonly selectionHighlight: boolean; readonly occurrencesHighlight: boolean; readonly codeLens: boolean; + readonly codeInsets: boolean; readonly folding: boolean; readonly foldingStrategy: 'auto' | 'indentation'; readonly showFoldingControls: 'always' | 'mouseover'; @@ -5414,6 +5420,20 @@ declare namespace monaco.languages { resolveCodeLens?(model: editor.ITextModel, codeLens: ICodeLensSymbol, token: CancellationToken): ProviderResult; } + export interface ICodeInsetSymbol { + range: IRange; + id?: string; + height?: number; + webviewHandle?: string; + } + + export interface CodeInsetProvider { + onDidChange?: IEvent; + extensionLocation: UriComponents; + provideCodeInsets(model: editor.ITextModel, token: CancellationToken): ProviderResult; + resolveCodeInset?(model: editor.ITextModel, codeInset: ICodeInsetSymbol, token: CancellationToken): ProviderResult; + } + export interface ILanguageExtensionPoint { id: string; extensions?: string[]; diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 978c31dd58c74..de92b6b7a3717 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -2235,6 +2235,24 @@ declare module 'vscode' { resolveCodeLens?(codeLens: CodeLens, token: CancellationToken): ProviderResult; } + + /** + */ + 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; + } + + /** * Information about where a symbol is defined. * @@ -7855,6 +7873,12 @@ declare module 'vscode' { */ export function registerCodeLensProvider(selector: DocumentSelector, provider: CodeLensProvider): Disposable; + /** + * Register a code inset provider. + * + */ + export function registerCodeInsetProvider(selector: DocumentSelector, provider: CodeInsetProvider): Disposable; + /** * Register a definition provider. * diff --git a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts index 48f55cbce28ba..6a23c63ccd7d3 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts @@ -11,7 +11,7 @@ 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 } from 'vs/editor/common/core/range'; -import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ISerializedLanguageConfiguration, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, LocationDto, WorkspaceSymbolDto, CodeActionDto, reviveWorkspaceEditDto, ISerializedDocumentFilter, DefinitionLinkDto, ISerializedSignatureHelpProviderMetadata } from '../node/extHost.protocol'; +import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ISerializedLanguageConfiguration, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, LocationDto, WorkspaceSymbolDto, CodeActionDto, reviveWorkspaceEditDto, ISerializedDocumentFilter, DefinitionLinkDto, ISerializedSignatureHelpProviderMetadata, CodeInsetDto } from '../node/extHost.protocol'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { LanguageConfiguration, IndentationRule, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration'; import { IHeapService } from './mainThreadHeapService'; @@ -20,6 +20,7 @@ import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostC import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters'; import { URI } from 'vs/base/common/uri'; import { Selection } from 'vs/editor/common/core/selection'; +import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; @extHostNamedCustomer(MainContext.MainThreadLanguageFeatures) export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesShape { @@ -160,6 +161,36 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha } } + // -- code inset + + $registerCodeInsetSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number, extension: IExtensionDescription): 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; + }); + }, + extensionLocation: extension.extensionLocation + }; + + if (typeof eventHandle === 'number') { + const emitter = new Emitter(); + this._registrations[eventHandle] = emitter; + provider.onDidChange = emitter.event; + } + + const langSelector = typeConverters.LanguageSelector.from(selector); + this._registrations[handle] = modes.CodeInsetProviderRegistry.register(langSelector, provider); + } + // --- declaration $registerDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void { diff --git a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts index d88f7ee8162ba..5b063318d5e62 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts @@ -22,9 +22,14 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { onUnexpectedError } from 'vs/base/common/errors'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; + +export let mainThreadWebviews: MainThreadWebviews; + @extHostNamedCustomer(MainContext.MainThreadWebviews) export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviver { + _serviceBrand: any; + private static readonly viewType = 'mainThreadWebview'; private static readonly standardSupportedLinkSchemes = ['http', 'https', 'mailto']; @@ -49,6 +54,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv @IExtensionService private readonly _extensionService: IExtensionService, @ITelemetryService private readonly _telemetryService: ITelemetryService ) { + mainThreadWebviews = this; this._proxy = context.getProxy(ExtHostContext.ExtHostWebviews); _editorService.onDidActiveEditorChange(this.onActiveEditorChanged, this, this._toDispose); _editorService.onDidVisibleEditorsChange(this.onVisibleEditorsChanged, this, this._toDispose); @@ -64,7 +70,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv this._toDispose = dispose(this._toDispose); } - public $createWebviewPanel( + public $createWebview( handle: WebviewPanelHandle, viewType: string, title: string, @@ -96,6 +102,23 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv this._telemetryService.publicLog('webviews:createWebviewPanel', { extensionId: extensionId.value }); } + public createInsetWebview( + handle: WebviewPanelHandle, + parent: HTMLElement, + options: WebviewInputOptions, + extensionLocation: UriComponents + ): WebviewEditorInput { + const webview = this._webviewService.createInsetWebview(parent, reviveWebviewOptions(options), URI.revive(extensionLocation), this.createWebviewEventDelegate(handle)); + webview.state = { + viewType: webview.viewType, + state: undefined + }; + + this._webviews.set(handle, webview); + this._activeWebview = handle; + return webview; + } + public $disposeWebview(handle: WebviewPanelHandle): void { const webview = this.getWebview(handle); webview.dispose(); @@ -289,7 +312,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv } } - private getWebview(handle: WebviewPanelHandle): WebviewEditorInput { + public getWebview(handle: WebviewPanelHandle): WebviewEditorInput { const webview = this._webviews.get(handle); if (!webview) { throw new Error('Unknown webview handle:' + handle); diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 1854d3639a21e..ab17d0ac50ea9 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -304,6 +304,9 @@ 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 { + return extHostLanguageFeatures.registerCodeInsetProvider(extension, checkSelector(selector), provider); + }, registerDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable { return extHostLanguageFeatures.registerDefinitionProvider(extension, checkSelector(selector), provider); }, @@ -754,6 +757,7 @@ export function createApiFactory( CodeActionKind: extHostTypes.CodeActionKind, CodeActionTrigger: extHostTypes.CodeActionTrigger, CodeLens: extHostTypes.CodeLens, + CodeInset: extHostTypes.CodeInset, Color: extHostTypes.Color, ColorInformation: extHostTypes.ColorInformation, ColorPresentation: extHostTypes.ColorPresentation, diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index a366eb27bda94..cee760792073c 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -299,6 +299,7 @@ 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, extension: IExtensionDescription): void; $emitCodeLensEvent(eventHandle: number, event?: any): void; $registerDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void; $registerDeclarationSupport(handle: number, selector: ISerializedDocumentFilter[]): void; @@ -473,7 +474,7 @@ export interface WebviewPanelShowOptions { } export interface MainThreadWebviewsShape extends IDisposable { - $createWebviewPanel(handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: vscode.WebviewPanelOptions & vscode.WebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): void; + $createWebview(handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: vscode.WebviewPanelOptions & vscode.WebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): void; $disposeWebview(handle: WebviewPanelHandle): void; $reveal(handle: WebviewPanelHandle, showOptions: WebviewPanelShowOptions): void; $setTitle(handle: WebviewPanelHandle, value: string): void; @@ -499,6 +500,18 @@ export interface ExtHostWebviewsShape { $deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: vscode.WebviewOptions): Promise; } +// export type CodeInsetWebviewHandle = string; + +// export interface ExtHostCodeInsetWebviewsShape { +// } + +// export interface ExtHostCodeInsetWebviewShape extends IDisposable { +// $setHtml(handle: WebviewPanelHandle, value: string): void; +// $setOptions(handle: WebviewPanelHandle, options: vscode.WebviewOptions): void; +// $postMessage(handle: WebviewPanelHandle, value: any): Promise; +// } + + export interface MainThreadUrlsShape extends IDisposable { $registerUriHandler(handle: number, extensionId: ExtensionIdentifier): Promise; $unregisterUriHandler(handle: number): Promise; @@ -892,10 +905,14 @@ export interface CodeLensDto extends ObjectIdentifier { command?: CommandDto; } +export type CodeInsetDto = ObjectIdentifier & modes.ICodeInsetSymbol; + export interface ExtHostLanguageFeaturesShape { $provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise; $provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise; $resolveCodeLens(handle: number, resource: UriComponents, 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; @@ -1157,6 +1174,7 @@ export const ExtHostContext = { ExtHostWorkspace: createExtId('ExtHostWorkspace'), ExtHostWindow: createExtId('ExtHostWindow'), ExtHostWebviews: createExtId('ExtHostWebviews'), + // ExtHostCodeInsetWebviews: createExtId('ExtHostWebviews'), ExtHostProgress: createMainId('ExtHostProgress'), ExtHostComments: createMainId('ExtHostComments'), ExtHostStorage: createMainId('ExtHostStorage'), diff --git a/src/vs/workbench/api/node/extHostApiCommands.ts b/src/vs/workbench/api/node/extHostApiCommands.ts index ef757ca94f004..fbee5b2cf771c 100644 --- a/src/vs/workbench/api/node/extHostApiCommands.ts +++ b/src/vs/workbench/api/node/extHostApiCommands.ts @@ -146,6 +146,13 @@ export class ExtHostApiCommands { ], returns: 'A promise that resolves to an array of CodeLens-instances.' }); + this._register('vscode.executeCodeInsetProvider', this._executeCodeInsetProvider, { + description: 'Execute CodeInset provider.', + args: [ + { name: 'uri', description: 'Uri of a text document', constraint: URI } + ], + returns: 'A promise that resolves to an array of CodeInset-instances.' + }); this._register('vscode.executeFormatDocumentProvider', this._executeFormatDocumentProvider, { description: 'Execute document format provider.', args: [ @@ -514,6 +521,14 @@ export class ExtHostApiCommands { } + private _executeCodeInsetProvider(resource: URI): Thenable { + const args = { resource }; + return this._commands.executeCommand('_executeCodeInsetProvider', args) + .then(tryMapWith(item => + new types.CodeInset( + typeConverters.Range.to(item.range)))); + } + private _executeFormatDocumentProvider(resource: URI, options: vscode.FormattingOptions): Promise { const args = { resource, diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index 9a1f33723b366..222f08069aa81 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -15,7 +15,7 @@ import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments'; import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHostCommands'; import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics'; import { asPromise } from 'vs/base/common/async'; -import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, WorkspaceSymbolDto, SuggestResultDto, WorkspaceSymbolsDto, SuggestionDto, CodeActionDto, ISerializedDocumentFilter, WorkspaceEditDto, ISerializedSignatureHelpProviderMetadata, LinkDto, CodeLensDto } from './extHost.protocol'; +import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, WorkspaceSymbolDto, SuggestResultDto, WorkspaceSymbolsDto, SuggestionDto, CodeActionDto, ISerializedDocumentFilter, WorkspaceEditDto, ISerializedSignatureHelpProviderMetadata, LinkDto, CodeLensDto, MainThreadWebviewsShape } 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'; @@ -26,6 +26,7 @@ import { IExtensionDescription } from 'vs/workbench/services/extensions/common/e import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ExtHostWebview } from 'vs/workbench/api/node/extHostWebview'; // --- adapter @@ -144,6 +145,52 @@ 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.getDocumentData(resource).document; + return asPromise(() => this._provider.provideCodeInsets(doc, token)).then(insets => { + if (Array.isArray(insets)) { + return insets.map(inset => { + const id = this._heapService.keep(inset); + return ObjectIdentifier.mixin({ + range: typeConvert.Range.from(inset.range), + height: inset.height + }, id); + }); + } else { + return undefined; + } + }); + } + + resolveCodeInset(symbol: modes.ICodeInsetSymbol, webview: vscode.Webview, token: CancellationToken): Promise { + + const inset = this._heapService.get(ObjectIdentifier.of(symbol)); + if (!inset) { + return undefined; + } + + let resolve: Promise; + if (typeof this._provider.resolveCodeInset !== 'function') { + resolve = Promise.resolve(inset); + } else { + resolve = asPromise(() => this._provider.resolveCodeInset(inset, webview, token)); + } + + return resolve.then(newInset => { + newInset = newInset || inset; + return symbol; + }); + } +} + function convertToLocationLinks(value: vscode.Definition): modes.LocationLink[] { if (Array.isArray(value)) { return (value as (vscode.DefinitionLink | vscode.Location)[]).map(typeConvert.DefinitionLink.from); @@ -916,7 +963,7 @@ type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | Hov | DocumentHighlightAdapter | ReferenceAdapter | CodeActionAdapter | DocumentFormattingAdapter | RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter | SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter - | ColorProviderAdapter | FoldingProviderAdapter | DeclarationAdapter | SelectionRangeAdapter; + | ColorProviderAdapter | FoldingProviderAdapter | CodeInsetAdapter | DeclarationAdapter | SelectionRangeAdapter; class AdapterData { constructor( @@ -941,6 +988,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { private _diagnostics: ExtHostDiagnostics; private _adapter = new Map(); private readonly _logService: ILogService; + private _webviewProxy: MainThreadWebviewsShape; constructor( mainContext: IMainContext, @@ -958,6 +1006,7 @@ 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): ISerializedDocumentFilter[] { @@ -1084,6 +1133,38 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.resolveCodeLens(URI.revive(resource), symbol, token)); } + // --- 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, extension); + let result = this._createDisposable(handle); + + if (eventHandle !== undefined) { + 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)); + } + + $resolveCodeInset(handle: number, resource: UriComponents, symbol: modes.ICodeInsetSymbol, token: CancellationToken): Promise { + const webview = new ExtHostWebview(symbol.webviewHandle, this._webviewProxy, { enableScripts: true }); + webview.html = ''; + const x = this._withAdapter(handle, CodeInsetAdapter, adapter => adapter.resolveCodeInset(symbol, webview, token)); + return x; + } + + $createCodeInsetWebview(handle: number) { + } + // --- declaration registerDefinitionProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable { diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index 08e61091cfd3a..f70a2acbe2b00 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -1136,6 +1136,21 @@ export class CodeLens { } } + +export class CodeInset { + + range: Range; + isResolved: boolean; + height?: number; + + constructor(range: Range, height?: number) { + this.range = range; + this.isResolved = false; + this.height = height; + } +} + + @es5ClassCompat export class MarkdownString { diff --git a/src/vs/workbench/api/node/extHostWebview.ts b/src/vs/workbench/api/node/extHostWebview.ts index f3851ed60885f..2dfa844024466 100644 --- a/src/vs/workbench/api/node/extHostWebview.ts +++ b/src/vs/workbench/api/node/extHostWebview.ts @@ -257,7 +257,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { }; const handle = ExtHostWebviews.newHandle(); - this._proxy.$createWebviewPanel(handle, viewType, title, webviewShowOptions, options, extension.identifier, extension.extensionLocation); + this._proxy.$createWebview(handle, viewType, title, webviewShowOptions, options, extension.identifier, extension.extensionLocation); const webview = new ExtHostWebview(handle, this._proxy, options); const panel = new ExtHostWebviewPanel(handle, this._proxy, viewType, title, viewColumn, options, webview); diff --git a/src/vs/workbench/browser/parts/quickinput/quickInputBox.ts b/src/vs/workbench/browser/parts/quickinput/quickInputBox.ts index 08ede924c14ea..0bc7afbd7132f 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInputBox.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInputBox.ts @@ -100,18 +100,18 @@ export class QuickInputBox { style(theme: ITheme) { this.inputBox.style({ - inputForeground: theme.getColor(inputForeground), - inputBackground: theme.getColor(inputBackground), - inputBorder: theme.getColor(inputBorder), - inputValidationInfoBackground: theme.getColor(inputValidationInfoBackground), - inputValidationInfoForeground: theme.getColor(inputValidationInfoForeground), - inputValidationInfoBorder: theme.getColor(inputValidationInfoBorder), - inputValidationWarningBackground: theme.getColor(inputValidationWarningBackground), - inputValidationWarningForeground: theme.getColor(inputValidationWarningForeground), - inputValidationWarningBorder: theme.getColor(inputValidationWarningBorder), - inputValidationErrorBackground: theme.getColor(inputValidationErrorBackground), - inputValidationErrorForeground: theme.getColor(inputValidationErrorForeground), - inputValidationErrorBorder: theme.getColor(inputValidationErrorBorder), + inputForeground: theme.getColor(inputForeground) || undefined, + inputBackground: theme.getColor(inputBackground) || undefined, + inputBorder: theme.getColor(inputBorder) || undefined, + inputValidationInfoBackground: theme.getColor(inputValidationInfoBackground) || undefined, + inputValidationInfoForeground: theme.getColor(inputValidationInfoForeground) || undefined, + inputValidationInfoBorder: theme.getColor(inputValidationInfoBorder) || undefined, + inputValidationWarningBackground: theme.getColor(inputValidationWarningBackground) || undefined, + inputValidationWarningForeground: theme.getColor(inputValidationWarningForeground) || undefined, + inputValidationWarningBorder: theme.getColor(inputValidationWarningBorder) || undefined, + inputValidationErrorBackground: theme.getColor(inputValidationErrorBackground) || undefined, + inputValidationErrorForeground: theme.getColor(inputValidationErrorForeground) || undefined, + inputValidationErrorBorder: theme.getColor(inputValidationErrorBorder) || undefined, }); } diff --git a/src/vs/workbench/contrib/files/electron-browser/views/explorerView.ts b/src/vs/workbench/contrib/files/electron-browser/views/explorerView.ts index bff9758fa3038..495d10b9fc403 100644 --- a/src/vs/workbench/contrib/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/electron-browser/views/explorerView.ts @@ -344,6 +344,11 @@ export class ExplorerView extends ViewletPanel { if (e.browserEvent instanceof MouseEvent) { isDoubleClick = e.browserEvent.detail === 2; + + if (!this.tree.openOnSingleClick && !isDoubleClick) { + return; + } + isMiddleClick = e.browserEvent.button === 1; const isLeftButton = e.browserEvent.button === 0; diff --git a/src/vs/workbench/contrib/scm/electron-browser/dirtydiffDecorator.ts b/src/vs/workbench/contrib/scm/electron-browser/dirtydiffDecorator.ts index 77f28716b6933..327f565414954 100644 --- a/src/vs/workbench/contrib/scm/electron-browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/electron-browser/dirtydiffDecorator.ts @@ -365,8 +365,8 @@ class DirtyDiffWidget extends PeekViewWidget { arrowColor: borderColor, frameColor: borderColor, headerBackgroundColor: theme.getColor(peekViewTitleBackground) || Color.transparent, - primaryHeadingColor: theme.getColor(peekViewTitleForeground), - secondaryHeadingColor: theme.getColor(peekViewTitleInfoForeground) + primaryHeadingColor: theme.getColor(peekViewTitleForeground) || undefined, + secondaryHeadingColor: theme.getColor(peekViewTitleInfoForeground) || undefined }); } diff --git a/src/vs/workbench/contrib/webview/electron-browser/webview-pre.js b/src/vs/workbench/contrib/webview/electron-browser/webview-pre.js index 5fb945732ccda..36d3759320d22 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webview-pre.js +++ b/src/vs/workbench/contrib/webview/electron-browser/webview-pre.js @@ -243,7 +243,7 @@ delete window.frameElement; `; - newDocument.head.prepend(defaultScript, newDocument.head.firstChild); + newDocument.head.prepend(defaultScript); } // apply default styles diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewEditorService.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewEditorService.ts index 10dbe724d1726..3bc8633774809 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewEditorService.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewEditorService.ts @@ -12,6 +12,8 @@ import * as vscode from 'vscode'; import { WebviewEditorInput } from './webviewEditorInput'; import { GroupIdentifier } from 'vs/workbench/common/editor'; import { equals } from 'vs/base/common/arrays'; +import { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/webviewElement'; +import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; export const IWebviewEditorService = createDecorator('webviewEditorService'); @@ -32,6 +34,13 @@ export interface IWebviewEditorService { events: WebviewEvents ): WebviewEditorInput; + createInsetWebview( + parent: HTMLElement, + options: vscode.WebviewOptions, + extensionLocation: URI, + events: WebviewEvents + ): WebviewEditorInput; + reviveWebview( viewType: string, id: number, @@ -97,6 +106,7 @@ export class WebviewEditorService implements IWebviewEditorService { @IEditorService private readonly _editorService: IEditorService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, + @IPartService private readonly _partService: IPartService, ) { } createWebview( @@ -112,6 +122,21 @@ export class WebviewEditorService implements IWebviewEditorService { return webviewInput; } + createInsetWebview( + parent: HTMLElement, + options: vscode.WebviewOptions, + extensionLocation: URI, + events: WebviewEvents + ): WebviewEditorInput { + const webviewEditorInput = this._instantiationService.createInstance(WebviewEditorInput, 'codeinset', undefined, '', options, {}, events, extensionLocation, undefined); + webviewEditorInput.webview = this._instantiationService.createInstance(WebviewElement, + this._partService.getContainer(Parts.EDITOR_PART), + { allowSvgs: true, useSameOriginForRoot: true }, + { allowScripts: true, disableFindView: true }); + webviewEditorInput.webview.mountTo(parent); + return webviewEditorInput; + } + revealWebview( webview: WebviewEditorInput, group: IEditorGroup, diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index 79cda62b62dbf..cc5a97d889889 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -13,12 +13,12 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import * as colorRegistry from 'vs/platform/theme/common/colorRegistry'; import { DARK, ITheme, IThemeService, LIGHT } from 'vs/platform/theme/common/themeService'; import { registerFileProtocol, WebviewProtocol } from 'vs/workbench/contrib/webview/electron-browser/webviewProtocols'; -import { areWebviewInputOptionsEqual } from './webviewEditorService'; import { WebviewFindWidget } from './webviewFindWidget'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { endsWith } from 'vs/base/common/strings'; import { isMacintosh } from 'vs/base/common/platform'; +import { equals } from 'vs/base/common/arrays'; export interface WebviewOptions { readonly allowSvgs?: boolean; @@ -30,8 +30,23 @@ export interface WebviewContentOptions { readonly allowScripts?: boolean; readonly svgWhiteList?: string[]; readonly localResourceRoots?: ReadonlyArray; + readonly disableFindView?: boolean; } + +export function areWebviewContentOptionsEqual(a: WebviewContentOptions, b: WebviewContentOptions): boolean { + const sameArray = (a1: ReadonlyArray, a2: ReadonlyArray) => + (a.localResourceRoots === b.localResourceRoots + || (Array.isArray(a.localResourceRoots) && Array.isArray(b.localResourceRoots) + && equals(a.localResourceRoots, b.localResourceRoots, (a, b) => a.toString() === b.toString()))); + + return a.allowScripts === b.allowScripts + && a.disableFindView === b.disableFindView + && sameArray(a.svgWhiteList, b.svgWhiteList) + && sameArray(a.localResourceRoots, b.localResourceRoots); +} + + interface IKeydownEvent { key: string; keyCode: number; @@ -248,6 +263,7 @@ export class WebviewElement extends Disposable { this._webview.setAttribute('partition', `webview${Date.now()}`); this._webview.setAttribute('webpreferences', 'contextIsolation=yes'); + this._webview.setAttribute('autosize', 'on'); this._webview.style.flex = '0 1'; this._webview.style.width = '0'; @@ -347,14 +363,18 @@ export class WebviewElement extends Disposable { this._send('devtools-opened'); })); - this._webviewFindWidget = this._register(instantiationService.createInstance(WebviewFindWidget, this)); + if (!this.options || !this.options.disableFindView) { + this._webviewFindWidget = this._register(instantiationService.createInstance(WebviewFindWidget, this)); + } this.style(this._themeService.getTheme()); this._register(this._themeService.onThemeChange(this.style, this)); } public mountTo(parent: HTMLElement) { - parent.appendChild(this._webviewFindWidget.getDomNode()!); + if (this._webviewFindWidget) { + parent.appendChild(this._webviewFindWidget.getDomNode()); + } parent.appendChild(this._webview); } @@ -382,6 +402,9 @@ export class WebviewElement extends Disposable { private readonly _onMessage = this._register(new Emitter()); public readonly onMessage = this._onMessage.event; + private readonly _onLayout = this._register(new Emitter<{ width: number, height: number }>()); + public readonly onLayout = this._onLayout.event; + private _send(channel: string, ...args: any[]): void { this._ready .then(() => this._webview.send(channel, ...args)) @@ -397,7 +420,7 @@ export class WebviewElement extends Disposable { } public set options(value: WebviewContentOptions) { - if (this._contentOptions && areWebviewInputOptionsEqual(value, this._contentOptions)) { + if (this._contentOptions && areWebviewContentOptionsEqual(value, this._contentOptions)) { return; } @@ -419,7 +442,7 @@ export class WebviewElement extends Disposable { } public update(value: string, options: WebviewContentOptions, retainContextWhenHidden: boolean) { - if (retainContextWhenHidden && value === this._contents && this._contentOptions && areWebviewInputOptionsEqual(options, this._contentOptions)) { + if (retainContextWhenHidden && value === this._contents && this._contentOptions && areWebviewContentOptionsEqual(options, this._contentOptions)) { return; } this._contents = value; @@ -482,8 +505,9 @@ export class WebviewElement extends Disposable { const activeTheme = ApiThemeClassName.fromTheme(theme); this._send('styles', styles, activeTheme); - this._webviewFindWidget.updateTheme(theme); - + if (this._webviewFindWidget) { + this._webviewFindWidget.updateTheme(theme); + } } public layout(): void { @@ -501,6 +525,11 @@ export class WebviewElement extends Disposable { } contents.setZoomFactor(factor); + if (!this._webview || !this._webview.parentElement) { + return; + } + + this._onLayout.fire({ width: this._webview.clientWidth, height: this._webview.clientHeight }); }); } @@ -551,11 +580,15 @@ export class WebviewElement extends Disposable { } public showFind() { - this._webviewFindWidget.reveal(); + if (this._webviewFindWidget) { + this._webviewFindWidget.reveal(); + } } public hideFind() { - this._webviewFindWidget.hide(); + if (this._webviewFindWidget) { + this._webviewFindWidget.hide(); + } } public reload() { diff --git a/src/vs/workbench/parts/codeinset/codeInset.contribution.ts b/src/vs/workbench/parts/codeinset/codeInset.contribution.ts new file mode 100644 index 0000000000000..813d6010a9d72 --- /dev/null +++ b/src/vs/workbench/parts/codeinset/codeInset.contribution.ts @@ -0,0 +1,335 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// import { Registry } from 'vs/platform/registry/common/platform'; +// import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; +// import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +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 } from 'vs/editor/common/modes'; +import { CodeInsetWidget, CodeInsetHelper } from './codeInsetWidget'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { getCodeInsetData, ICodeInsetData } from './codeinset'; +import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { MainThreadWebviews } from 'vs/workbench/api/electron-browser/mainThreadWebview'; + +export class CodeInsetController implements editorCommon.IEditorContribution { + + private static readonly ID: string = 'css.editor.codeInset'; + + private _isEnabled: boolean; + + private _globalToDispose: IDisposable[]; + private _localToDispose: IDisposable[]; + private _insetWidgets: CodeInsetWidget[]; + private _currentFindCodeInsetSymbolsPromise: CancelablePromise; + private _modelChangeCounter: number; + private _currentResolveCodeInsetSymbolsPromise: CancelablePromise; + private _detectVisibleInsets: RunOnceScheduler; + private _mainThreadWebviews: MainThreadWebviews; + + constructor( + private _editor: editorBrowser.ICodeEditor, + @ICommandService private readonly _commandService: ICommandService, + @INotificationService private readonly _notificationService: INotificationService, + @IExtensionService private readonly _extensionService: IExtensionService + ) { + this._isEnabled = this._editor.getConfiguration().contribInfo.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._editor.onDidChangeConfiguration(() => { + let prevIsEnabled = this._isEnabled; + this._isEnabled = this._editor.getConfiguration().contribInfo.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); + } + + 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, viewZoneChangeAccessor: editorBrowser.IViewZoneChangeAccessor): 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.getModel()) { + return; + } + + let maxLineNumber = this._editor.getModel().getLineCount(); + let groups: ICodeInsetData[][] = []; + let lastGroup: ICodeInsetData[]; + + 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, + this._commandService, this._notificationService, + () => this._detectVisibleInsets.schedule())); + 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, + this._commandService, this._notificationService, + () => this._detectVisibleInsets.schedule())); + groupsIndex++; + } + + helper.commit(changeAccessor); + }); + }); + + scrollState.restore(this._editor); + } + + + private getWebviewService(): MainThreadWebviews { + if (!this._mainThreadWebviews) { + this._mainThreadWebviews = this._extensionService.getNamedCustomer('MainThreadWebviews'); + } + return this._mainThreadWebviews; + } + + + private _onViewportChanged(): void { + if (this._currentResolveCodeInsetSymbolsPromise) { + this._currentResolveCodeInsetSymbolsPromise.cancel(); + 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 => { + const symbol = request.symbol; + if (typeof request.provider.resolveCodeInset === 'function') { + const mainThreadWebviews = this.getWebviewService(); + symbol.webviewHandle = insetWidgets[r].createWebview(mainThreadWebviews, request.provider.extensionLocation); + return request.provider.resolveCodeInset(model, symbol, token); + } + return Promise.resolve(void 0); + }); + + return Promise.all(widgetPromises); + }); + + return Promise.all(allPromises); + }); + + this._currentResolveCodeInsetSymbolsPromise.then(() => { + this._currentResolveCodeInsetSymbolsPromise = null; + }).catch(err => { + this._currentResolveCodeInsetSymbolsPromise = null; + onUnexpectedError(err); + }); + } +} + +registerEditorContribution(CodeInsetController); diff --git a/src/vs/workbench/parts/codeinset/codeInset.ts b/src/vs/workbench/parts/codeinset/codeInset.ts new file mode 100644 index 0000000000000..2382d61098576 --- /dev/null +++ b/src/vs/workbench/parts/codeinset/codeInset.ts @@ -0,0 +1,87 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions'; +import { ICodeInsetSymbol, CodeInsetProvider, CodeInsetProviderRegistry } from 'vs/editor/common/modes'; +import { ITextModel } from 'vs/editor/common/model'; +import { onUnexpectedExternalError, illegalArgument } from 'vs/base/common/errors'; +import { mergeSort } from 'vs/base/common/arrays'; +import { URI } from 'vs/base/common/uri'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { CancellationToken } from 'vs/base/common/cancellation'; + +export interface ICodeInsetData { + symbol: ICodeInsetSymbol; + provider: CodeInsetProvider; +} + +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; + } + }); + }); +} + +registerLanguageCommand('_executeCodeInsetProvider', function (accessor, args) { + let { resource, itemResolveCount } = args; + if (!(resource instanceof URI)) { + throw illegalArgument(); + } + + const model = accessor.get(IModelService).getModel(resource); + if (!model) { + throw illegalArgument(); + } + + const result: ICodeInsetSymbol[] = []; + return getCodeInsetData(model, CancellationToken.None).then(value => { + + let resolve: Thenable[] = []; + + for (const item of value) { + if (typeof itemResolveCount === 'undefined') { + result.push(item.symbol); + } else if (itemResolveCount-- > 0) { + resolve.push(Promise.resolve(item.provider.resolveCodeInset(model, item.symbol, CancellationToken.None)) + .then(symbol => result.push(symbol))); + } + } + + return Promise.all(resolve); + + }).then(() => { + return result; + }); +}); diff --git a/src/vs/workbench/parts/codeinset/codeInsetWidget.css b/src/vs/workbench/parts/codeinset/codeInsetWidget.css new file mode 100644 index 0000000000000..113a5a1fbb440 --- /dev/null +++ b/src/vs/workbench/parts/codeinset/codeInsetWidget.css @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .codelens-decoration { + overflow: hidden; + display: inline-block; + text-overflow: ellipsis; +} + +.monaco-editor .codelens-decoration > span, +.monaco-editor .codelens-decoration > a { + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + white-space: nowrap; + vertical-align: sub; +} + +.monaco-editor .codelens-decoration > a { + text-decoration: none; +} + +.monaco-editor .codelens-decoration > a:hover { + text-decoration: underline; + cursor: pointer; +} + +.monaco-editor .codelens-decoration.invisible-cl { + opacity: 0; +} + +@keyframes fadein { 0% { opacity:0; visibility:visible;} 100% { opacity:1; } } +@-moz-keyframes fadein { 0% { opacity:0; visibility:visible;} 100% { opacity:1; } } +@-o-keyframes fadein { 0% { opacity:0; visibility:visible;} 100% { opacity:1; } } +@-webkit-keyframes fadein { 0% { opacity:0; visibility:visible;} 100% { opacity:1; } } + +.monaco-editor .codelens-decoration.fadein { + -webkit-animation: fadein 0.5s linear; + -moz-animation: fadein 0.5s linear; + -o-animation: fadein 0.5s linear; + animation: fadein 0.5s linear; +} diff --git a/src/vs/workbench/parts/codeinset/codeInsetWidget.ts b/src/vs/workbench/parts/codeinset/codeInsetWidget.ts new file mode 100644 index 0000000000000..6b67ac7b5b3a7 --- /dev/null +++ b/src/vs/workbench/parts/codeinset/codeInsetWidget.ts @@ -0,0 +1,191 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { ICommandService } from 'vs/platform/commands/common/commands'; +import { Range } from 'vs/editor/common/core/range'; +import * as editorBrowser from 'vs/editor/browser/editorBrowser'; +import { ICodeInsetData } from './codeInset'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; +import { IModelDeltaDecoration, IModelDecorationsChangeAccessor, ITextModel } from 'vs/editor/common/model'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { WebviewEditorInput } from 'vs/workbench/contrib/webview/electron-browser/webviewEditorInput'; +import { mainThreadWebviews, MainThreadWebviews } from 'vs/workbench/api/electron-browser/mainThreadWebview'; +import { UriComponents } from 'vs/base/common/uri'; + + +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 _viewZone: editorBrowser.IViewZone; + private _viewZoneId?: number = undefined; + private _decorationIds: string[]; + private _data: ICodeInsetData[]; + private _webview: WebviewEditorInput | undefined; + private _webviewHandle: string | undefined; + private _range: Range; + + constructor( + data: ICodeInsetData[], // all the insets on the same line (often just one) + editor: editorBrowser.ICodeEditor, + helper: CodeInsetHelper, + commandService: ICommandService, + notificationService: INotificationService, + updateCallabck: Function + ) { + 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): void { + console.log('DISPOSE'); + while (this._decorationIds.length) { + helper.removeDecoration(this._decorationIds.pop()); + } + if (viewZoneChangeAccessor) { + viewZoneChangeAccessor.removeZone(this._viewZoneId); + this._viewZone = undefined; + } + } + + public isValid(): boolean { + return 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) { + helper.removeDecoration(this._decorationIds.pop()); + } + 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 { + const range = this._editor.getModel().getDecorationRange(this._decorationIds[0]); + if (range) { + return range.startLineNumber; + } + return -1; + } + + public get webview() { return this._webview; } + + static webviewPool = 1; + + public createWebview(mainThreadWebview: MainThreadWebviews, extensionLocation: UriComponents) { + if (this._webviewHandle) { return this._webviewHandle; } + + const lineNumber = this._range.endLineNumber; + this._editor.changeViewZones(accessor => { + if (this._viewZoneId) { + this._webview.dispose(); + accessor.removeZone(this._viewZoneId); + } + const div = document.createElement('div'); + + this._webviewHandle = CodeInsetWidget.webviewPool++ + ''; + this._webview = mainThreadWebviews.createInsetWebview(this._webviewHandle, div, { enableScripts: true }, extensionLocation); + + const webview = this._webview.webview; + 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 => { + accessor.layoutZone(this._viewZoneId); + }); + } + }); + + this._viewZone = { + afterLineNumber: lineNumber, + heightInPx: 50, + domNode: div + }; + this._viewZoneId = accessor.addZone(this._viewZone); + }); + return this._webviewHandle; + } + + public reposition(viewZoneChangeAccessor: editorBrowser.IViewZoneChangeAccessor): void { + if (this.isValid() && this._editor.hasModel()) { + const range = this._editor.getModel().getDecorationRange(this._decorationIds[0]); + this._viewZone.afterLineNumber = range.endLineNumber; + viewZoneChangeAccessor.layoutZone(this._viewZoneId); + } + } +} diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index d40c975542cf9..247fcde06fffe 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -222,6 +222,9 @@ export interface IExtensionService extends ICpuProfilerTarget { * Stops the extension host. */ stopExtensionHost(): void; + + getNamedCustomer?(sid: string): any; + } export interface ICpuProfilerTarget { diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts index 277ad1cbd429e..f3a9fe81a7794 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts @@ -48,6 +48,7 @@ export class ExtensionHostProcessManager extends Disposable { private _extensionHostProcessRPCProtocol: RPCProtocol; private readonly _extensionHostProcessCustomers: IDisposable[]; private readonly _extensionHostProcessWorker: IExtensionHostStarter; + private readonly _namedCustomerById: { [sid: string]: any } = {}; /** * winjs believes a proxy is a promise because it has a `then` method, so wrap the result in an object. */ @@ -183,6 +184,7 @@ export class ExtensionHostProcessManager extends Disposable { const instance = this._instantiationService.createInstance(ctor, extHostContext); this._extensionHostProcessCustomers.push(instance); this._extensionHostProcessRPCProtocol.set(id, instance); + this._namedCustomerById[id.sid] = instance; } // Customers @@ -205,6 +207,10 @@ export class ExtensionHostProcessManager extends Disposable { }); } + public getNamedCustomer(sid: string): any { + return this._namedCustomerById[sid]; + } + public activateByEvent(activationEvent: string): Promise { if (this._extensionHostProcessFinishedActivateEvents[activationEvent] || !this._extensionHostProcessProxy) { return NO_OP_VOID_PROMISE; diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index d7d69bb07c911..2dd7b5fe86b67 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -417,6 +417,11 @@ export class ExtensionService extends Disposable implements IExtensionService { this._stopExtensionHostProcess(); } + public getNamedCustomer(sid: string): any { + const ncs = this._extensionHostProcessManagers.map(m => m.getNamedCustomer(sid)).filter(c => c); + return ncs.length ? ncs[0] : undefined; + } + private _stopExtensionHostProcess(): void { let previouslyActivatedExtensionIds: ExtensionIdentifier[] = []; this._extensionHostActiveExtensions.forEach((value) => { diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index debe4a9b00320..f63e71db1cd15 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -175,6 +175,10 @@ import 'vs/workbench/contrib/outline/browser/outline.contribution'; // Experiments import 'vs/workbench/contrib/experiments/electron-browser/experiments.contribution'; +// Code Insets +import 'vs/workbench/parts/codeinset/codeInset.contribution'; + +//#endregion // Issues import 'vs/workbench/contrib/issue/electron-browser/issue.contribution'; From f7bd7ff049f19f05095e8dc82a4c946650a5504e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 12 Feb 2019 14:46:58 +0100 Subject: [PATCH 02/14] undo some changes --- .../contrib/gotoError/gotoErrorWidget.ts | 2 +- .../referenceSearch/referencesWidget.ts | 4 ++-- .../api/electron-browser/mainThreadWebview.ts | 2 +- .../browser/parts/quickinput/quickInputBox.ts | 24 +++++++++---------- .../electron-browser/views/explorerView.ts | 5 ---- .../electron-browser/dirtydiffDecorator.ts | 4 ++-- src/vs/workbench/workbench.main.ts | 1 - 7 files changed, 18 insertions(+), 24 deletions(-) diff --git a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts index 74e424d84c287..fd3598b5a2840 100644 --- a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts +++ b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts @@ -188,7 +188,7 @@ export class MarkerNavigationWidget extends ZoneWidget { } private _applyTheme(theme: ITheme) { - this._backgroundColor = theme.getColor(editorMarkerNavigationBackground) || undefined; + this._backgroundColor = theme.getColor(editorMarkerNavigationBackground); let colorId = editorMarkerNavigationError; if (this._severity === MarkerSeverity.Warning) { colorId = editorMarkerNavigationWarning; diff --git a/src/vs/editor/contrib/referenceSearch/referencesWidget.ts b/src/vs/editor/contrib/referenceSearch/referencesWidget.ts index 23068af099a49..4fd0bbebf594b 100644 --- a/src/vs/editor/contrib/referenceSearch/referencesWidget.ts +++ b/src/vs/editor/contrib/referenceSearch/referencesWidget.ts @@ -279,8 +279,8 @@ export class ReferenceWidget extends PeekViewWidget { arrowColor: borderColor, frameColor: borderColor, headerBackgroundColor: theme.getColor(peekViewTitleBackground) || Color.transparent, - primaryHeadingColor: theme.getColor(peekViewTitleForeground) || undefined, - secondaryHeadingColor: theme.getColor(peekViewTitleInfoForeground) || undefined + primaryHeadingColor: theme.getColor(peekViewTitleForeground), + secondaryHeadingColor: theme.getColor(peekViewTitleInfoForeground) }); } diff --git a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts index 5b063318d5e62..8f2803c533f46 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts @@ -22,7 +22,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { onUnexpectedError } from 'vs/base/common/errors'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; - +// todo@joh not nice! export let mainThreadWebviews: MainThreadWebviews; @extHostNamedCustomer(MainContext.MainThreadWebviews) diff --git a/src/vs/workbench/browser/parts/quickinput/quickInputBox.ts b/src/vs/workbench/browser/parts/quickinput/quickInputBox.ts index 0bc7afbd7132f..08ede924c14ea 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInputBox.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInputBox.ts @@ -100,18 +100,18 @@ export class QuickInputBox { style(theme: ITheme) { this.inputBox.style({ - inputForeground: theme.getColor(inputForeground) || undefined, - inputBackground: theme.getColor(inputBackground) || undefined, - inputBorder: theme.getColor(inputBorder) || undefined, - inputValidationInfoBackground: theme.getColor(inputValidationInfoBackground) || undefined, - inputValidationInfoForeground: theme.getColor(inputValidationInfoForeground) || undefined, - inputValidationInfoBorder: theme.getColor(inputValidationInfoBorder) || undefined, - inputValidationWarningBackground: theme.getColor(inputValidationWarningBackground) || undefined, - inputValidationWarningForeground: theme.getColor(inputValidationWarningForeground) || undefined, - inputValidationWarningBorder: theme.getColor(inputValidationWarningBorder) || undefined, - inputValidationErrorBackground: theme.getColor(inputValidationErrorBackground) || undefined, - inputValidationErrorForeground: theme.getColor(inputValidationErrorForeground) || undefined, - inputValidationErrorBorder: theme.getColor(inputValidationErrorBorder) || undefined, + inputForeground: theme.getColor(inputForeground), + inputBackground: theme.getColor(inputBackground), + inputBorder: theme.getColor(inputBorder), + inputValidationInfoBackground: theme.getColor(inputValidationInfoBackground), + inputValidationInfoForeground: theme.getColor(inputValidationInfoForeground), + inputValidationInfoBorder: theme.getColor(inputValidationInfoBorder), + inputValidationWarningBackground: theme.getColor(inputValidationWarningBackground), + inputValidationWarningForeground: theme.getColor(inputValidationWarningForeground), + inputValidationWarningBorder: theme.getColor(inputValidationWarningBorder), + inputValidationErrorBackground: theme.getColor(inputValidationErrorBackground), + inputValidationErrorForeground: theme.getColor(inputValidationErrorForeground), + inputValidationErrorBorder: theme.getColor(inputValidationErrorBorder), }); } diff --git a/src/vs/workbench/contrib/files/electron-browser/views/explorerView.ts b/src/vs/workbench/contrib/files/electron-browser/views/explorerView.ts index 495d10b9fc403..bff9758fa3038 100644 --- a/src/vs/workbench/contrib/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/electron-browser/views/explorerView.ts @@ -344,11 +344,6 @@ export class ExplorerView extends ViewletPanel { if (e.browserEvent instanceof MouseEvent) { isDoubleClick = e.browserEvent.detail === 2; - - if (!this.tree.openOnSingleClick && !isDoubleClick) { - return; - } - isMiddleClick = e.browserEvent.button === 1; const isLeftButton = e.browserEvent.button === 0; diff --git a/src/vs/workbench/contrib/scm/electron-browser/dirtydiffDecorator.ts b/src/vs/workbench/contrib/scm/electron-browser/dirtydiffDecorator.ts index 327f565414954..77f28716b6933 100644 --- a/src/vs/workbench/contrib/scm/electron-browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/electron-browser/dirtydiffDecorator.ts @@ -365,8 +365,8 @@ class DirtyDiffWidget extends PeekViewWidget { arrowColor: borderColor, frameColor: borderColor, headerBackgroundColor: theme.getColor(peekViewTitleBackground) || Color.transparent, - primaryHeadingColor: theme.getColor(peekViewTitleForeground) || undefined, - secondaryHeadingColor: theme.getColor(peekViewTitleInfoForeground) || undefined + primaryHeadingColor: theme.getColor(peekViewTitleForeground), + secondaryHeadingColor: theme.getColor(peekViewTitleInfoForeground) }); } diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index f63e71db1cd15..53ebf1f6b901a 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -178,7 +178,6 @@ import 'vs/workbench/contrib/experiments/electron-browser/experiments.contributi // Code Insets import 'vs/workbench/parts/codeinset/codeInset.contribution'; -//#endregion // Issues import 'vs/workbench/contrib/issue/electron-browser/issue.contribution'; From d02c8011df08c0f9526c699c122aaf1d8200c8ba Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 12 Feb 2019 14:50:18 +0100 Subject: [PATCH 03/14] mark API as proposed and enforce proposedApi-property --- src/vs/vscode.d.ts | 24 ---------------- src/vs/vscode.proposed.d.ts | 28 +++++++++++++++++++ src/vs/workbench/api/node/extHost.api.impl.ts | 1 + 3 files changed, 29 insertions(+), 24 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index de92b6b7a3717..978c31dd58c74 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -2235,24 +2235,6 @@ declare module 'vscode' { resolveCodeLens?(codeLens: CodeLens, token: CancellationToken): ProviderResult; } - - /** - */ - 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; - } - - /** * Information about where a symbol is defined. * @@ -7873,12 +7855,6 @@ declare module 'vscode' { */ export function registerCodeLensProvider(selector: DocumentSelector, provider: CodeLensProvider): Disposable; - /** - * Register a code inset provider. - * - */ - export function registerCodeInsetProvider(selector: DocumentSelector, provider: CodeInsetProvider): Disposable; - /** * Register a definition provider. * diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 2ad45164371c4..ed37fac7b1d59 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -33,6 +33,34 @@ declare module 'vscode' { export namespace workspace { export function registerRemoteAuthorityResolver(authorityPrefix: string, resolver: RemoteAuthorityResolver): Disposable; + + } + //#endregion + + + // #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 namespace languages { + + /** + * Register a code inset provider. + * + */ + export function registerCodeInsetProvider(selector: DocumentSelector, provider: CodeInsetProvider): Disposable; } //#endregion diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index ab17d0ac50ea9..a3dc698c3b065 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -305,6 +305,7 @@ export function createApiFactory( 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 { From bc659cf39fd5d7888cd4a668c30e7f5c6cc3ab05 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 12 Feb 2019 15:00:25 +0100 Subject: [PATCH 04/14] move editor.codeInsets setting to its contribution because it isn't a monaco editor thing --- src/vs/editor/common/config/editorOptions.ts | 9 ----- src/vs/monaco.d.ts | 6 ---- .../parts/codeinset/codeInset.contribution.ts | 33 +++++++++++++++---- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index ad23683521479..d8ee1d9226bba 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -600,11 +600,6 @@ export interface IEditorOptions { * Defaults to true. */ codeLens?: boolean; - /** - * Show code insets - * Defaults to true. - */ - codeInsets?: boolean; /** * Control the behavior and rendering of the code action lightbulb. */ @@ -1001,7 +996,6 @@ export interface EditorContribOptions { readonly selectionHighlight: boolean; readonly occurrencesHighlight: boolean; readonly codeLens: boolean; - readonly codeInsets: boolean; readonly folding: boolean; readonly foldingStrategy: 'auto' | 'indentation'; readonly showFoldingControls: 'always' | 'mouseover'; @@ -2057,7 +2051,6 @@ export class EditorOptionsValidator { selectionHighlight: _boolean(opts.selectionHighlight, defaults.selectionHighlight), occurrencesHighlight: _boolean(opts.occurrencesHighlight, defaults.occurrencesHighlight), codeLens: _boolean(opts.codeLens, defaults.codeLens), - codeInsets: _boolean(opts.codeInsets, defaults.codeInsets), folding: _boolean(opts.folding, defaults.folding), foldingStrategy: _stringSet<'auto' | 'indentation'>(opts.foldingStrategy, defaults.foldingStrategy, ['auto', 'indentation']), showFoldingControls: _stringSet<'always' | 'mouseover'>(opts.showFoldingControls, defaults.showFoldingControls, ['always', 'mouseover']), @@ -2171,7 +2164,6 @@ export class InternalEditorOptionsFactory { selectionHighlight: (accessibilityIsOn ? false : opts.contribInfo.selectionHighlight), // DISABLED WHEN SCREEN READER IS ATTACHED occurrencesHighlight: (accessibilityIsOn ? false : opts.contribInfo.occurrencesHighlight), // DISABLED WHEN SCREEN READER IS ATTACHED codeLens: (accessibilityIsOn ? false : opts.contribInfo.codeLens), // DISABLED WHEN SCREEN READER IS ATTACHED - codeInsets: (accessibilityIsOn ? false : opts.contribInfo.codeInsets), folding: (accessibilityIsOn ? false : opts.contribInfo.folding), // DISABLED WHEN SCREEN READER IS ATTACHED foldingStrategy: opts.contribInfo.foldingStrategy, showFoldingControls: opts.contribInfo.showFoldingControls, @@ -2661,7 +2653,6 @@ export const EDITOR_DEFAULTS: IValidatedEditorOptions = { selectionHighlight: true, occurrencesHighlight: true, codeLens: true, - codeInsets: true, folding: true, foldingStrategy: 'auto', showFoldingControls: 'mouseover', diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index f3f4f799dc4ba..9d854d9c2da80 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -2934,11 +2934,6 @@ declare namespace monaco.editor { * Defaults to true. */ codeLens?: boolean; - /** - * Show code insets - * Defaults to true. - */ - codeInsets?: boolean; /** * Control the behavior and rendering of the code action lightbulb. */ @@ -3276,7 +3271,6 @@ declare namespace monaco.editor { readonly selectionHighlight: boolean; readonly occurrencesHighlight: boolean; readonly codeLens: boolean; - readonly codeInsets: boolean; readonly folding: boolean; readonly foldingStrategy: 'auto' | 'indentation'; readonly showFoldingControls: 'always' | 'mouseover'; diff --git a/src/vs/workbench/parts/codeinset/codeInset.contribution.ts b/src/vs/workbench/parts/codeinset/codeInset.contribution.ts index 813d6010a9d72..163905639c14b 100644 --- a/src/vs/workbench/parts/codeinset/codeInset.contribution.ts +++ b/src/vs/workbench/parts/codeinset/codeInset.contribution.ts @@ -21,6 +21,10 @@ import { getCodeInsetData, ICodeInsetData } from './codeinset'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { MainThreadWebviews } from 'vs/workbench/api/electron-browser/mainThreadWebview'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; +import { localize } from 'vs/nls.mock'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export class CodeInsetController implements editorCommon.IEditorContribution { @@ -41,9 +45,10 @@ export class CodeInsetController implements editorCommon.IEditorContribution { private _editor: editorBrowser.ICodeEditor, @ICommandService private readonly _commandService: ICommandService, @INotificationService private readonly _notificationService: INotificationService, - @IExtensionService private readonly _extensionService: IExtensionService + @IExtensionService private readonly _extensionService: IExtensionService, + @IConfigurationService private readonly _configService: IConfigurationService, ) { - this._isEnabled = this._editor.getConfiguration().contribInfo.codeInsets; + this._isEnabled = this._configService.getValue('editor.codeInsets'); this._globalToDispose = []; this._localToDispose = []; @@ -53,11 +58,13 @@ export class CodeInsetController implements editorCommon.IEditorContribution { this._globalToDispose.push(this._editor.onDidChangeModel(() => this._onModelChange())); this._globalToDispose.push(this._editor.onDidChangeModelLanguage(() => this._onModelChange())); - this._globalToDispose.push(this._editor.onDidChangeConfiguration(() => { - let prevIsEnabled = this._isEnabled; - this._isEnabled = this._editor.getConfiguration().contribInfo.codeInsets; - if (prevIsEnabled !== this._isEnabled) { - 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)); @@ -333,3 +340,15 @@ export class CodeInsetController implements editorCommon.IEditorContribution { } 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 + } + } +}); From 8d35ebb7d8e1d97c6100ce06db346ad85d89b91e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 12 Feb 2019 15:20:18 +0100 Subject: [PATCH 05/14] remove MainThreadWebviews hack --- src/vs/workbench/api/electron-browser/mainThreadWebview.ts | 3 --- src/vs/workbench/parts/codeinset/codeInsetWidget.ts | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts index 8f2803c533f46..7a88fc7e38951 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts @@ -22,8 +22,6 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { onUnexpectedError } from 'vs/base/common/errors'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -// todo@joh not nice! -export let mainThreadWebviews: MainThreadWebviews; @extHostNamedCustomer(MainContext.MainThreadWebviews) export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviver { @@ -54,7 +52,6 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv @IExtensionService private readonly _extensionService: IExtensionService, @ITelemetryService private readonly _telemetryService: ITelemetryService ) { - mainThreadWebviews = this; this._proxy = context.getProxy(ExtHostContext.ExtHostWebviews); _editorService.onDidActiveEditorChange(this.onActiveEditorChanged, this, this._toDispose); _editorService.onDidVisibleEditorsChange(this.onVisibleEditorsChanged, this, this._toDispose); diff --git a/src/vs/workbench/parts/codeinset/codeInsetWidget.ts b/src/vs/workbench/parts/codeinset/codeInsetWidget.ts index 6b67ac7b5b3a7..dca58609fdc29 100644 --- a/src/vs/workbench/parts/codeinset/codeInsetWidget.ts +++ b/src/vs/workbench/parts/codeinset/codeInsetWidget.ts @@ -12,7 +12,7 @@ import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { IModelDeltaDecoration, IModelDecorationsChangeAccessor, ITextModel } from 'vs/editor/common/model'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { WebviewEditorInput } from 'vs/workbench/contrib/webview/electron-browser/webviewEditorInput'; -import { mainThreadWebviews, MainThreadWebviews } from 'vs/workbench/api/electron-browser/mainThreadWebview'; +import { MainThreadWebviews } from 'vs/workbench/api/electron-browser/mainThreadWebview'; import { UriComponents } from 'vs/base/common/uri'; @@ -144,7 +144,7 @@ export class CodeInsetWidget { static webviewPool = 1; - public createWebview(mainThreadWebview: MainThreadWebviews, extensionLocation: UriComponents) { + public createWebview(mainThreadWebviews: MainThreadWebviews, extensionLocation: UriComponents) { if (this._webviewHandle) { return this._webviewHandle; } const lineNumber = this._range.endLineNumber; From ace73c05b7b7f289ac565d7cb3737acf0e0a7d00 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 12 Feb 2019 15:47:37 +0100 Subject: [PATCH 06/14] move rest code code inset to workbench out of (monaco) editor --- src/vs/editor/common/modes.ts | 20 +--------------- src/vs/monaco.d.ts | 14 ----------- .../mainThreadLanguageFeatures.ts | 7 +++--- src/vs/workbench/api/node/extHost.protocol.ts | 3 ++- .../workbench/api/node/extHostApiCommands.ts | 3 ++- .../api/node/extHostLanguageFeatures.ts | 9 ++++---- .../parts/codeinset/codeInset.contribution.ts | 6 +---- src/vs/workbench/parts/codeinset/codeInset.ts | 23 +++++++++++++++++-- 8 files changed, 36 insertions(+), 49 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 895cbc0a20603..6126701924d3a 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -9,7 +9,7 @@ import { Event } from 'vs/base/common/event'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { IDisposable } from 'vs/base/common/lifecycle'; import { isObject } from 'vs/base/common/types'; -import { URI, UriComponents } from 'vs/base/common/uri'; +import { URI } from 'vs/base/common/uri'; import { Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; @@ -1321,19 +1321,6 @@ export interface CodeLensProvider { resolveCodeLens?(model: model.ITextModel, codeLens: ICodeLensSymbol, token: CancellationToken): ProviderResult; } -export interface ICodeInsetSymbol { - range: IRange; - id?: string; - height?: number; - webviewHandle?: string; -} -export interface CodeInsetProvider { - onDidChange?: Event; - extensionLocation: UriComponents; - provideCodeInsets(model: model.ITextModel, token: CancellationToken): ProviderResult; - resolveCodeInset?(model: model.ITextModel, codeInset: ICodeInsetSymbol, token: CancellationToken): ProviderResult; -} - // --- feature registries ------ /** @@ -1396,11 +1383,6 @@ export const TypeDefinitionProviderRegistry = new LanguageFeatureRegistry(); -/** - * @internal - */ -export const CodeInsetProviderRegistry = new LanguageFeatureRegistry(); - /** * @internal */ diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 9d854d9c2da80..b994864125b56 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -5414,20 +5414,6 @@ declare namespace monaco.languages { resolveCodeLens?(model: editor.ITextModel, codeLens: ICodeLensSymbol, token: CancellationToken): ProviderResult; } - export interface ICodeInsetSymbol { - range: IRange; - id?: string; - height?: number; - webviewHandle?: string; - } - - export interface CodeInsetProvider { - onDidChange?: IEvent; - extensionLocation: UriComponents; - provideCodeInsets(model: editor.ITextModel, token: CancellationToken): ProviderResult; - resolveCodeInset?(model: editor.ITextModel, codeInset: ICodeInsetSymbol, token: CancellationToken): ProviderResult; - } - export interface ILanguageExtensionPoint { id: string; extensions?: string[]; diff --git a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts index 6a23c63ccd7d3..a02156f510998 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts @@ -21,6 +21,7 @@ import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters'; import { URI } from 'vs/base/common/uri'; import { Selection } from 'vs/editor/common/core/selection'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import * as codeInset from 'vs/workbench/parts/codeinset/codeInset'; @extHostNamedCustomer(MainContext.MainThreadLanguageFeatures) export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesShape { @@ -165,7 +166,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha $registerCodeInsetSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number, extension: IExtensionDescription): void { - const provider = { + 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)); } @@ -182,13 +183,13 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha }; if (typeof eventHandle === 'number') { - const emitter = new Emitter(); + const emitter = new Emitter(); this._registrations[eventHandle] = emitter; provider.onDidChange = emitter.event; } const langSelector = typeConverters.LanguageSelector.from(selector); - this._registrations[handle] = modes.CodeInsetProviderRegistry.register(langSelector, provider); + this._registrations[handle] = codeInset.CodeInsetProviderRegistry.register(langSelector, provider); } // --- declaration diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index cee760792073c..ba2cfb9011b03 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -45,6 +45,7 @@ import { IMarkdownString } from 'vs/base/common/htmlContent'; import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IRemoteConsoleLog } from 'vs/base/node/console'; +import * as codeInset from 'vs/workbench/parts/codeinset/codeInset'; export interface IEnvironment { isExtensionDevelopmentDebug: boolean; @@ -905,7 +906,7 @@ export interface CodeLensDto extends ObjectIdentifier { command?: CommandDto; } -export type CodeInsetDto = ObjectIdentifier & modes.ICodeInsetSymbol; +export type CodeInsetDto = ObjectIdentifier & codeInset.ICodeInsetSymbol; export interface ExtHostLanguageFeaturesShape { $provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise; diff --git a/src/vs/workbench/api/node/extHostApiCommands.ts b/src/vs/workbench/api/node/extHostApiCommands.ts index fbee5b2cf771c..1155f36e80f43 100644 --- a/src/vs/workbench/api/node/extHostApiCommands.ts +++ b/src/vs/workbench/api/node/extHostApiCommands.ts @@ -18,6 +18,7 @@ import { CustomCodeAction } from 'vs/workbench/api/node/extHostLanguageFeatures' import { ICommandsExecutor, PreviewHTMLAPICommand, OpenFolderAPICommand, DiffAPICommand, OpenAPICommand, RemoveFromRecentlyOpenedAPICommand, SetEditorLayoutAPICommand } from './apiCommands'; import { EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService'; import { isFalsyOrEmpty } from 'vs/base/common/arrays'; +import * as codeInset from 'vs/workbench/parts/codeinset/codeInset'; export class ExtHostApiCommands { @@ -523,7 +524,7 @@ export class ExtHostApiCommands { private _executeCodeInsetProvider(resource: URI): Thenable { const args = { resource }; - return this._commands.executeCommand('_executeCodeInsetProvider', args) + return this._commands.executeCommand('_executeCodeInsetProvider', args) .then(tryMapWith(item => new types.CodeInset( typeConverters.Range.to(item.range)))); diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index 222f08069aa81..5f1fbdcff17f5 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -27,6 +27,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { ExtHostWebview } from 'vs/workbench/api/node/extHostWebview'; +import * as codeInset from 'vs/workbench/parts/codeinset/codeInset'; // --- adapter @@ -153,7 +154,7 @@ class CodeInsetAdapter { private readonly _provider: vscode.CodeInsetProvider ) { } - provideCodeInsets(resource: URI, token: CancellationToken): Promise { + provideCodeInsets(resource: URI, token: CancellationToken): Promise { const doc = this._documents.getDocumentData(resource).document; return asPromise(() => this._provider.provideCodeInsets(doc, token)).then(insets => { if (Array.isArray(insets)) { @@ -170,7 +171,7 @@ class CodeInsetAdapter { }); } - resolveCodeInset(symbol: modes.ICodeInsetSymbol, webview: vscode.Webview, token: CancellationToken): Promise { + resolveCodeInset(symbol: codeInset.ICodeInsetSymbol, webview: vscode.Webview, token: CancellationToken): Promise { const inset = this._heapService.get(ObjectIdentifier.of(symbol)); if (!inset) { @@ -1151,11 +1152,11 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return result; } - $provideCodeInsets(handle: number, resource: UriComponents, token: CancellationToken): Promise { + $provideCodeInsets(handle: number, resource: UriComponents, token: CancellationToken): Promise { return this._withAdapter(handle, CodeInsetAdapter, adapter => adapter.provideCodeInsets(URI.revive(resource), token)); } - $resolveCodeInset(handle: number, resource: UriComponents, symbol: modes.ICodeInsetSymbol, token: CancellationToken): Promise { + $resolveCodeInset(handle: number, resource: UriComponents, symbol: codeInset.ICodeInsetSymbol, token: CancellationToken): Promise { const webview = new ExtHostWebview(symbol.webviewHandle, this._webviewProxy, { enableScripts: true }); webview.html = ''; const x = this._withAdapter(handle, CodeInsetAdapter, adapter => adapter.resolveCodeInset(symbol, webview, token)); diff --git a/src/vs/workbench/parts/codeinset/codeInset.contribution.ts b/src/vs/workbench/parts/codeinset/codeInset.contribution.ts index 163905639c14b..7114923e1ad89 100644 --- a/src/vs/workbench/parts/codeinset/codeInset.contribution.ts +++ b/src/vs/workbench/parts/codeinset/codeInset.contribution.ts @@ -3,9 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -// import { Registry } from 'vs/platform/registry/common/platform'; -// import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; -// import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; 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'; @@ -13,11 +10,10 @@ 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 } from 'vs/editor/common/modes'; +import { CodeInsetProviderRegistry, getCodeInsetData, ICodeInsetData } from './codeInset'; import { CodeInsetWidget, CodeInsetHelper } from './codeInsetWidget'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { getCodeInsetData, ICodeInsetData } from './codeinset'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { MainThreadWebviews } from 'vs/workbench/api/electron-browser/mainThreadWebview'; diff --git a/src/vs/workbench/parts/codeinset/codeInset.ts b/src/vs/workbench/parts/codeinset/codeInset.ts index 2382d61098576..2301776cb185d 100644 --- a/src/vs/workbench/parts/codeinset/codeInset.ts +++ b/src/vs/workbench/parts/codeinset/codeInset.ts @@ -4,13 +4,32 @@ *--------------------------------------------------------------------------------------------*/ import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions'; -import { ICodeInsetSymbol, CodeInsetProvider, CodeInsetProviderRegistry } from 'vs/editor/common/modes'; import { ITextModel } from 'vs/editor/common/model'; import { onUnexpectedExternalError, illegalArgument } from 'vs/base/common/errors'; import { mergeSort } from 'vs/base/common/arrays'; -import { URI } from 'vs/base/common/uri'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { Event } from 'vs/base/common/event'; import { IModelService } from 'vs/editor/common/services/modelService'; 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 { + range: IRange; + id?: string; + height?: number; + webviewHandle?: string; +} + +export interface CodeInsetProvider { + onDidChange?: Event; + extensionLocation: UriComponents; + provideCodeInsets(model: ITextModel, token: CancellationToken): ProviderResult; + resolveCodeInset?(model: ITextModel, codeInset: ICodeInsetSymbol, token: CancellationToken): ProviderResult; +} + +export const CodeInsetProviderRegistry = new LanguageFeatureRegistry(); export interface ICodeInsetData { symbol: ICodeInsetSymbol; From 8246c1648fb2bc1d63740e967c1827fa6be69ea4 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 12 Feb 2019 15:48:46 +0100 Subject: [PATCH 07/14] workbenchs/parts became workbench/contrib --- .../api/electron-browser/mainThreadLanguageFeatures.ts | 2 +- src/vs/workbench/api/node/extHost.protocol.ts | 2 +- src/vs/workbench/api/node/extHostApiCommands.ts | 2 +- src/vs/workbench/api/node/extHostLanguageFeatures.ts | 2 +- .../{parts => contrib}/codeinset/codeInset.contribution.ts | 0 src/vs/workbench/{parts => contrib}/codeinset/codeInset.ts | 0 .../workbench/{parts => contrib}/codeinset/codeInsetWidget.css | 0 .../workbench/{parts => contrib}/codeinset/codeInsetWidget.ts | 0 src/vs/workbench/workbench.main.ts | 2 +- 9 files changed, 5 insertions(+), 5 deletions(-) rename src/vs/workbench/{parts => contrib}/codeinset/codeInset.contribution.ts (100%) rename src/vs/workbench/{parts => contrib}/codeinset/codeInset.ts (100%) rename src/vs/workbench/{parts => contrib}/codeinset/codeInsetWidget.css (100%) rename src/vs/workbench/{parts => contrib}/codeinset/codeInsetWidget.ts (100%) diff --git a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts index a02156f510998..1aa547ec9ad24 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts @@ -21,7 +21,7 @@ import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters'; import { URI } from 'vs/base/common/uri'; import { Selection } from 'vs/editor/common/core/selection'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; -import * as codeInset from 'vs/workbench/parts/codeinset/codeInset'; +import * as codeInset from 'vs/workbench/contrib/codeinset/codeInset'; @extHostNamedCustomer(MainContext.MainThreadLanguageFeatures) export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesShape { diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index ba2cfb9011b03..de93ffd6f8eae 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -45,7 +45,7 @@ import { IMarkdownString } from 'vs/base/common/htmlContent'; import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IRemoteConsoleLog } from 'vs/base/node/console'; -import * as codeInset from 'vs/workbench/parts/codeinset/codeInset'; +import * as codeInset from 'vs/workbench/contrib/codeinset/codeInset'; export interface IEnvironment { isExtensionDevelopmentDebug: boolean; diff --git a/src/vs/workbench/api/node/extHostApiCommands.ts b/src/vs/workbench/api/node/extHostApiCommands.ts index 1155f36e80f43..a47fd3c72f68c 100644 --- a/src/vs/workbench/api/node/extHostApiCommands.ts +++ b/src/vs/workbench/api/node/extHostApiCommands.ts @@ -18,7 +18,7 @@ import { CustomCodeAction } from 'vs/workbench/api/node/extHostLanguageFeatures' import { ICommandsExecutor, PreviewHTMLAPICommand, OpenFolderAPICommand, DiffAPICommand, OpenAPICommand, RemoveFromRecentlyOpenedAPICommand, SetEditorLayoutAPICommand } from './apiCommands'; import { EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService'; import { isFalsyOrEmpty } from 'vs/base/common/arrays'; -import * as codeInset from 'vs/workbench/parts/codeinset/codeInset'; +import * as codeInset from 'vs/workbench/contrib/codeinset/codeInset'; export class ExtHostApiCommands { diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index 5f1fbdcff17f5..906da259deae9 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -27,7 +27,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { ExtHostWebview } from 'vs/workbench/api/node/extHostWebview'; -import * as codeInset from 'vs/workbench/parts/codeinset/codeInset'; +import * as codeInset from 'vs/workbench/contrib/codeinset/codeInset'; // --- adapter diff --git a/src/vs/workbench/parts/codeinset/codeInset.contribution.ts b/src/vs/workbench/contrib/codeinset/codeInset.contribution.ts similarity index 100% rename from src/vs/workbench/parts/codeinset/codeInset.contribution.ts rename to src/vs/workbench/contrib/codeinset/codeInset.contribution.ts diff --git a/src/vs/workbench/parts/codeinset/codeInset.ts b/src/vs/workbench/contrib/codeinset/codeInset.ts similarity index 100% rename from src/vs/workbench/parts/codeinset/codeInset.ts rename to src/vs/workbench/contrib/codeinset/codeInset.ts diff --git a/src/vs/workbench/parts/codeinset/codeInsetWidget.css b/src/vs/workbench/contrib/codeinset/codeInsetWidget.css similarity index 100% rename from src/vs/workbench/parts/codeinset/codeInsetWidget.css rename to src/vs/workbench/contrib/codeinset/codeInsetWidget.css diff --git a/src/vs/workbench/parts/codeinset/codeInsetWidget.ts b/src/vs/workbench/contrib/codeinset/codeInsetWidget.ts similarity index 100% rename from src/vs/workbench/parts/codeinset/codeInsetWidget.ts rename to src/vs/workbench/contrib/codeinset/codeInsetWidget.ts diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index 53ebf1f6b901a..50f88b96a4b45 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -176,7 +176,7 @@ import 'vs/workbench/contrib/outline/browser/outline.contribution'; import 'vs/workbench/contrib/experiments/electron-browser/experiments.contribution'; // Code Insets -import 'vs/workbench/parts/codeinset/codeInset.contribution'; +import 'vs/workbench/contrib/codeinset/codeInset.contribution'; // Issues import 'vs/workbench/contrib/issue/electron-browser/issue.contribution'; From aa204b03762b649c1f6e6ab93880085fbb4147b5 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 12 Feb 2019 18:56:42 +0100 Subject: [PATCH 08/14] fix - keep webview alive when scrolling --- src/vs/workbench/contrib/codeinset/codeInset.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/codeinset/codeInset.contribution.ts b/src/vs/workbench/contrib/codeinset/codeInset.contribution.ts index 7114923e1ad89..7e20339e1de6b 100644 --- a/src/vs/workbench/contrib/codeinset/codeInset.contribution.ts +++ b/src/vs/workbench/contrib/codeinset/codeInset.contribution.ts @@ -312,7 +312,7 @@ export class CodeInsetController implements editorCommon.IEditorContribution { const widgetPromises = widgetRequests.map(request => { const symbol = request.symbol; - if (typeof request.provider.resolveCodeInset === 'function') { + if (!symbol.webviewHandle && typeof request.provider.resolveCodeInset === 'function') { const mainThreadWebviews = this.getWebviewService(); symbol.webviewHandle = insetWidgets[r].createWebview(mainThreadWebviews, request.provider.extensionLocation); return request.provider.resolveCodeInset(model, symbol, token); From 44b75883624f88e9af21e55c22397512aa2af9cd Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 13 Feb 2019 12:41:05 +0100 Subject: [PATCH 09/14] remove getNamedCustomer, connect MainThreadWebview with CodeInsetController --- src/vs/vscode.proposed.d.ts | 2 +- .../api/electron-browser/mainThreadWebview.ts | 111 ++++++++++++------ src/vs/workbench/api/node/extHost.api.impl.ts | 2 +- src/vs/workbench/api/node/extHost.protocol.ts | 12 +- .../api/node/extHostLanguageFeatures.ts | 49 ++++---- src/vs/workbench/api/node/extHostWebview.ts | 10 +- .../codeinset/codeInset.contribution.ts | 68 +++++------ .../workbench/contrib/codeinset/codeInset.ts | 6 +- .../contrib/codeinset/codeInsetWidget.ts | 37 ++---- .../electron-browser/webviewEditorService.ts | 27 +---- .../services/extensions/common/extensions.ts | 5 +- .../electron-browser/extensionService.ts | 5 - 12 files changed, 165 insertions(+), 169 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index ed37fac7b1d59..2cfff0cea1a57 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -51,7 +51,7 @@ declare module 'vscode' { export interface CodeInsetProvider { onDidChangeCodeInsets?: Event; provideCodeInsets(document: TextDocument, token: CancellationToken): ProviderResult; - resolveCodeInset?(codeInset: CodeInset, webview: Webview, token: CancellationToken): ProviderResult; + resolveCodeInset(codeInset: CodeInset, webview: Webview, token: CancellationToken): ProviderResult; } export namespace languages { diff --git a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts index 7a88fc7e38951..03b880ecec225 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts @@ -8,7 +8,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelShowOptions } from 'vs/workbench/api/node/extHost.protocol'; +import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelShowOptions, WebviewInsetHandle } from 'vs/workbench/api/node/extHost.protocol'; import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/shared/editor'; import { WebviewEditor } from 'vs/workbench/contrib/webview/electron-browser/webviewEditor'; import { WebviewEditorInput } from 'vs/workbench/contrib/webview/electron-browser/webviewEditorInput'; @@ -21,7 +21,11 @@ import { extHostNamedCustomer } from './extHostCustomers'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { onUnexpectedError } from 'vs/base/common/errors'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; - +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/webviewElement'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { CodeInsetController } from 'vs/workbench/contrib/codeinset/codeInset.contribution'; +import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; @extHostNamedCustomer(MainContext.MainThreadWebviews) export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviver { @@ -38,6 +42,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv private readonly _proxy: ExtHostWebviewsShape; private readonly _webviews = new Map(); + private readonly _webviewsElements = new Map(); private readonly _revivers = new Set(); private _activeWebview: WebviewPanelHandle | undefined = undefined; @@ -50,7 +55,10 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv @IWebviewEditorService private readonly _webviewService: IWebviewEditorService, @IOpenerService private readonly _openerService: IOpenerService, @IExtensionService private readonly _extensionService: IExtensionService, - @ITelemetryService private readonly _telemetryService: ITelemetryService + @ITelemetryService private readonly _telemetryService: ITelemetryService, + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @ICodeEditorService private readonly _codeEditorService: ICodeEditorService, + @IPartService private readonly _partService: IPartService, ) { this._proxy = context.getProxy(ExtHostContext.ExtHostWebviews); _editorService.onDidActiveEditorChange(this.onActiveEditorChanged, this, this._toDispose); @@ -67,7 +75,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv this._toDispose = dispose(this._toDispose); } - public $createWebview( + public $createWebviewPanel( handle: WebviewPanelHandle, viewType: string, title: string, @@ -99,21 +107,42 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv this._telemetryService.publicLog('webviews:createWebviewPanel', { extensionId: extensionId.value }); } - public createInsetWebview( - handle: WebviewPanelHandle, - parent: HTMLElement, - options: WebviewInputOptions, - extensionLocation: UriComponents - ): WebviewEditorInput { - const webview = this._webviewService.createInsetWebview(parent, reviveWebviewOptions(options), URI.revive(extensionLocation), this.createWebviewEventDelegate(handle)); - webview.state = { - viewType: webview.viewType, - state: undefined - }; + $createWebviewCodeInset(handle: WebviewInsetHandle, symbolId: string, options: vscode.WebviewOptions, extensionLocation: UriComponents): void { + // todo@joh main is for the lack of a code-inset service + // which we maybe wanna have... this is how it now works + // 1) create webview element + // 2) find the code inset controller that request it + // 3) let the controller adopt the widget + // 4) continue to forward messages to the webview + const webview = this._instantiationService.createInstance( + WebviewElement, + this._partService.getContainer(Parts.EDITOR_PART), + { + useSameOriginForRoot: true, + extensionLocation: URI.revive(extensionLocation) + }, + { + allowScripts: options.enableScripts + } + ); + + let found = false; + for (const editor of this._codeEditorService.listCodeEditors()) { + const ctrl = CodeInsetController.get(editor); + if (ctrl && ctrl.acceptWebview(symbolId, webview)) { + found = true; + break; + } + } - this._webviews.set(handle, webview); - this._activeWebview = handle; - return webview; + if (!found) { + webview.dispose(); + return; + } + // this will leak... the adopted webview will be disposed by the + // code inset controller. we might need a dispose-event here so that + // we can clean up things. + this._webviewsElements.set(handle, webview); } public $disposeWebview(handle: WebviewPanelHandle): void { @@ -131,14 +160,22 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv webview.iconPath = reviveWebviewIcon(value); } - public $setHtml(handle: WebviewPanelHandle, value: string): void { - const webview = this.getWebview(handle); - webview.html = value; + public $setHtml(handle: WebviewPanelHandle | WebviewInsetHandle, value: string): void { + if (typeof handle === 'number') { + this._webviewsElements.get(handle).contents = value; + } else { + const webview = this.getWebview(handle); + webview.html = value; + } } - public $setOptions(handle: WebviewPanelHandle, options: vscode.WebviewOptions): void { - const webview = this.getWebview(handle); - webview.setOptions(reviveWebviewOptions(options)); + public $setOptions(handle: WebviewPanelHandle | WebviewInsetHandle, options: vscode.WebviewOptions): void { + if (typeof handle === 'number') { + this._webviewsElements.get(handle).options = reviveWebviewOptions(options); + } else { + const webview = this.getWebview(handle); + webview.setOptions(reviveWebviewOptions(options)); + } } public $reveal(handle: WebviewPanelHandle, showOptions: WebviewPanelShowOptions): void { @@ -152,18 +189,24 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv this._webviewService.revealWebview(webview, targetGroup || this._editorGroupService.getGroup(webview.group), !!showOptions.preserveFocus); } - public $postMessage(handle: WebviewPanelHandle, message: any): Promise { - const webview = this.getWebview(handle); - const editors = this._editorService.visibleControls - .filter(e => e instanceof WebviewEditor) - .map(e => e as WebviewEditor) - .filter(e => e.input!.matches(webview)); + public $postMessage(handle: WebviewPanelHandle | WebviewInsetHandle, message: any): Promise { + if (typeof handle === 'number') { + this._webviewsElements.get(handle).sendMessage(message); + return Promise.resolve(true); - for (const editor of editors) { - editor.sendMessage(message); - } + } else { + const webview = this.getWebview(handle); + const editors = this._editorService.visibleControls + .filter(e => e instanceof WebviewEditor) + .map(e => e as WebviewEditor) + .filter(e => e.input!.matches(webview)); + + for (const editor of editors) { + editor.sendMessage(message); + } - return Promise.resolve(editors.length > 0); + return Promise.resolve(editors.length > 0); + } } public $registerSerializer(viewType: string): void { diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index a3dc698c3b065..c590299823d93 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -478,7 +478,7 @@ export function createApiFactory( return extHostOutputService.createOutputChannel(name); }, createWebviewPanel(viewType: string, title: string, showOptions: vscode.ViewColumn | { viewColumn: vscode.ViewColumn, preserveFocus?: boolean }, options: vscode.WebviewPanelOptions & vscode.WebviewOptions): vscode.WebviewPanel { - return extHostWebviews.createWebview(extension, viewType, title, showOptions, options); + return extHostWebviews.createWebviewPanel(extension, viewType, title, showOptions, options); }, createTerminal(nameOrOptions: vscode.TerminalOptions | string, shellPath?: string, shellArgs?: string[]): vscode.Terminal { if (typeof nameOrOptions === 'object') { diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index de93ffd6f8eae..dadca886aa0b1 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -469,20 +469,24 @@ export interface MainThreadTelemetryShape extends IDisposable { export type WebviewPanelHandle = string; +export type WebviewInsetHandle = number; + export interface WebviewPanelShowOptions { readonly viewColumn?: EditorViewColumn; readonly preserveFocus?: boolean; } export interface MainThreadWebviewsShape extends IDisposable { - $createWebview(handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: vscode.WebviewPanelOptions & vscode.WebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): void; + $createWebviewPanel(handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: vscode.WebviewPanelOptions & vscode.WebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): void; + $createWebviewCodeInset(handle: WebviewInsetHandle, symbolId: string, options: vscode.WebviewOptions, extensionLocation: UriComponents): void; $disposeWebview(handle: WebviewPanelHandle): void; $reveal(handle: WebviewPanelHandle, showOptions: WebviewPanelShowOptions): void; $setTitle(handle: WebviewPanelHandle, value: string): void; $setIconPath(handle: WebviewPanelHandle, value: { light: UriComponents, dark: UriComponents } | undefined): void; - $setHtml(handle: WebviewPanelHandle, value: string): void; - $setOptions(handle: WebviewPanelHandle, options: vscode.WebviewOptions): void; - $postMessage(handle: WebviewPanelHandle, value: any): Promise; + + $setHtml(handle: WebviewPanelHandle | WebviewInsetHandle, value: string): void; + $setOptions(handle: WebviewPanelHandle | WebviewInsetHandle, options: vscode.WebviewOptions): void; + $postMessage(handle: WebviewPanelHandle | WebviewInsetHandle, value: any): Promise; $registerSerializer(viewType: string): void; $unregisterSerializer(viewType: string): void; diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index 906da259deae9..15a14ef8e6e6f 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -15,7 +15,7 @@ import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments'; import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHostCommands'; import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics'; import { asPromise } from 'vs/base/common/async'; -import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, WorkspaceSymbolDto, SuggestResultDto, WorkspaceSymbolsDto, SuggestionDto, CodeActionDto, ISerializedDocumentFilter, WorkspaceEditDto, ISerializedSignatureHelpProviderMetadata, LinkDto, CodeLensDto, MainThreadWebviewsShape } from './extHost.protocol'; +import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, WorkspaceSymbolDto, SuggestResultDto, WorkspaceSymbolsDto, SuggestionDto, CodeActionDto, ISerializedDocumentFilter, WorkspaceEditDto, ISerializedSignatureHelpProviderMetadata, LinkDto, CodeLensDto, MainThreadWebviewsShape, CodeInsetDto } from './extHost.protocol'; import { regExpLeadsToEndlessLoop, regExpFlags } from 'vs/base/common/strings'; import { IPosition } from 'vs/editor/common/core/position'; import { IRange, Range as EditorRange } from 'vs/editor/common/core/range'; @@ -28,6 +28,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { ExtHostWebview } from 'vs/workbench/api/node/extHostWebview'; import * as codeInset from 'vs/workbench/contrib/codeinset/codeInset'; +import { generateUuid } from 'vs/base/common/uuid'; // --- adapter @@ -154,38 +155,33 @@ class CodeInsetAdapter { private readonly _provider: vscode.CodeInsetProvider ) { } - provideCodeInsets(resource: URI, token: CancellationToken): Promise { + provideCodeInsets(resource: URI, token: CancellationToken): Promise { const doc = this._documents.getDocumentData(resource).document; return asPromise(() => this._provider.provideCodeInsets(doc, token)).then(insets => { if (Array.isArray(insets)) { return insets.map(inset => { - const id = this._heapService.keep(inset); - return ObjectIdentifier.mixin({ + const $ident = this._heapService.keep(inset); + const id = generateUuid(); + return { + $ident, + id, range: typeConvert.Range.from(inset.range), height: inset.height - }, id); + }; }); - } else { - return undefined; } + return undefined; }); } - resolveCodeInset(symbol: codeInset.ICodeInsetSymbol, webview: vscode.Webview, token: CancellationToken): Promise { + resolveCodeInset(symbol: CodeInsetDto, webview: vscode.Webview, token: CancellationToken): Promise { const inset = this._heapService.get(ObjectIdentifier.of(symbol)); if (!inset) { - return undefined; - } - - let resolve: Promise; - if (typeof this._provider.resolveCodeInset !== 'function') { - resolve = Promise.resolve(inset); - } else { - resolve = asPromise(() => this._provider.resolveCodeInset(inset, webview, token)); + return Promise.resolve(symbol); } - return resolve.then(newInset => { + return asPromise(() => this._provider.resolveCodeInset(inset, webview, token)).then(newInset => { newInset = newInset || inset; return symbol; }); @@ -1057,7 +1053,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return ExtHostLanguageFeatures._handlePool++; } - private _withAdapter(handle: number, ctor: { new(...args: any[]): A }, callback: (adapter: A) => Promise): Promise { + private _withAdapter(handle: number, ctor: { new(...args: any[]): A }, callback: (adapter: A, extenson: IExtensionDescription) => Promise): Promise { const data = this._adapter.get(handle); if (!data) { return Promise.reject(new Error('no adapter found')); @@ -1069,7 +1065,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { t1 = Date.now(); this._logService.trace(`[${data.extension.identifier.value}] INVOKE provider '${(ctor as any).name}'`); } - let p = callback(data.adapter); + let p = callback(data.adapter, data.extension); const extension = data.extension; if (extension) { Promise.resolve(p).then( @@ -1156,14 +1152,13 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._withAdapter(handle, CodeInsetAdapter, adapter => adapter.provideCodeInsets(URI.revive(resource), token)); } - $resolveCodeInset(handle: number, resource: UriComponents, symbol: codeInset.ICodeInsetSymbol, token: CancellationToken): Promise { - const webview = new ExtHostWebview(symbol.webviewHandle, this._webviewProxy, { enableScripts: true }); - webview.html = ''; - const x = this._withAdapter(handle, CodeInsetAdapter, adapter => adapter.resolveCodeInset(symbol, webview, token)); - return x; - } - - $createCodeInsetWebview(handle: number) { + $resolveCodeInset(handle: number, _resource: UriComponents, symbol: codeInset.ICodeInsetSymbol, token: CancellationToken): Promise { + const webviewHandle = Math.random(); + const webview = new ExtHostWebview(webviewHandle, this._webviewProxy, { enableScripts: true }); + return this._withAdapter(handle, CodeInsetAdapter, async (adapter, extension) => { + await this._webviewProxy.$createWebviewCodeInset(webviewHandle, symbol.id, { enableCommandUris: true, enableScripts: true }, extension.extensionLocation); + return adapter.resolveCodeInset(symbol, webview, token); + }); } // --- declaration diff --git a/src/vs/workbench/api/node/extHostWebview.ts b/src/vs/workbench/api/node/extHostWebview.ts index 2dfa844024466..65fab620a3606 100644 --- a/src/vs/workbench/api/node/extHostWebview.ts +++ b/src/vs/workbench/api/node/extHostWebview.ts @@ -8,14 +8,14 @@ import { URI } from 'vs/base/common/uri'; import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters'; import { EditorViewColumn } from 'vs/workbench/api/shared/editor'; import * as vscode from 'vscode'; -import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewState } from './extHost.protocol'; +import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewState, WebviewInsetHandle } from './extHost.protocol'; import { Disposable } from './extHostTypes'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; type IconPath = URI | { light: URI, dark: URI }; export class ExtHostWebview implements vscode.Webview { - private readonly _handle: WebviewPanelHandle; + private readonly _handle: WebviewPanelHandle | WebviewInsetHandle; private readonly _proxy: MainThreadWebviewsShape; private _html: string; private _options: vscode.WebviewOptions; @@ -25,7 +25,7 @@ export class ExtHostWebview implements vscode.Webview { public readonly onDidReceiveMessage: Event = this._onMessageEmitter.event; constructor( - handle: WebviewPanelHandle, + handle: WebviewPanelHandle | WebviewInsetHandle, proxy: MainThreadWebviewsShape, options: vscode.WebviewOptions ) { @@ -243,7 +243,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { this._proxy = mainContext.getProxy(MainContext.MainThreadWebviews); } - public createWebview( + public createWebviewPanel( extension: IExtensionDescription, viewType: string, title: string, @@ -257,7 +257,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { }; const handle = ExtHostWebviews.newHandle(); - this._proxy.$createWebview(handle, viewType, title, webviewShowOptions, options, extension.identifier, extension.extensionLocation); + this._proxy.$createWebviewPanel(handle, viewType, title, webviewShowOptions, options, extension.identifier, extension.extensionLocation); const webview = new ExtHostWebview(handle, this._proxy, options); const panel = new ExtHostWebviewPanel(handle, this._proxy, viewType, title, viewColumn, options, webview); diff --git a/src/vs/workbench/contrib/codeinset/codeInset.contribution.ts b/src/vs/workbench/contrib/codeinset/codeInset.contribution.ts index 7e20339e1de6b..57e84679755aa 100644 --- a/src/vs/workbench/contrib/codeinset/codeInset.contribution.ts +++ b/src/vs/workbench/contrib/codeinset/codeInset.contribution.ts @@ -12,18 +12,19 @@ import * as editorCommon from 'vs/editor/common/editorCommon'; import { IModelDecorationsChangeAccessor } from 'vs/editor/common/model'; import { CodeInsetProviderRegistry, getCodeInsetData, ICodeInsetData } from './codeInset'; import { CodeInsetWidget, CodeInsetHelper } from './codeInsetWidget'; -import { ICommandService } from 'vs/platform/commands/common/commands'; -import { INotificationService } from 'vs/platform/notification/common/notification'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { MainThreadWebviews } from 'vs/workbench/api/electron-browser/mainThreadWebview'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import { localize } from 'vs/nls.mock'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/webviewElement'; export class CodeInsetController implements editorCommon.IEditorContribution { + static get(editor: editorBrowser.ICodeEditor): CodeInsetController { + return editor.getContribution(CodeInsetController.ID); + } + private static readonly ID: string = 'css.editor.codeInset'; private _isEnabled: boolean; @@ -31,17 +32,14 @@ export class CodeInsetController implements editorCommon.IEditorContribution { private _globalToDispose: IDisposable[]; private _localToDispose: IDisposable[]; private _insetWidgets: CodeInsetWidget[]; + private _pendingWebviews = new Map any>(); private _currentFindCodeInsetSymbolsPromise: CancelablePromise; private _modelChangeCounter: number; private _currentResolveCodeInsetSymbolsPromise: CancelablePromise; private _detectVisibleInsets: RunOnceScheduler; - private _mainThreadWebviews: MainThreadWebviews; constructor( private _editor: editorBrowser.ICodeEditor, - @ICommandService private readonly _commandService: ICommandService, - @INotificationService private readonly _notificationService: INotificationService, - @IExtensionService private readonly _extensionService: IExtensionService, @IConfigurationService private readonly _configService: IConfigurationService, ) { this._isEnabled = this._configService.getValue('editor.codeInsets'); @@ -72,6 +70,15 @@ export class CodeInsetController implements editorCommon.IEditorContribution { this._globalToDispose = dispose(this._globalToDispose); } + acceptWebview(symbolId: string, webviewElement: WebviewElement): boolean { + if (this._pendingWebviews.has(symbolId)) { + this._pendingWebviews.get(symbolId)(webviewElement); + this._pendingWebviews.delete(symbolId); + return true; + } + return false; + } + private _localDispose(): void { if (this._currentFindCodeInsetSymbolsPromise) { this._currentFindCodeInsetSymbolsPromise.cancel(); @@ -239,11 +246,11 @@ export class CodeInsetController implements editorCommon.IEditorContribution { groupsIndex++; codeInsetIndex++; } else { - this._insetWidgets.splice(codeInsetIndex, 0, - new CodeInsetWidget(groups[groupsIndex], - this._editor, helper, - this._commandService, this._notificationService, - () => this._detectVisibleInsets.schedule())); + this._insetWidgets.splice( + codeInsetIndex, + 0, + new CodeInsetWidget(groups[groupsIndex], this._editor, helper) + ); codeInsetIndex++; groupsIndex++; } @@ -257,11 +264,10 @@ export class CodeInsetController implements editorCommon.IEditorContribution { // Create extra symbols while (groupsIndex < groups.length) { - this._insetWidgets.push( - new CodeInsetWidget(groups[groupsIndex], - this._editor, helper, - this._commandService, this._notificationService, - () => this._detectVisibleInsets.schedule())); + this._insetWidgets.push(new CodeInsetWidget( + groups[groupsIndex], + this._editor, helper + )); groupsIndex++; } @@ -272,15 +278,6 @@ export class CodeInsetController implements editorCommon.IEditorContribution { scrollState.restore(this._editor); } - - private getWebviewService(): MainThreadWebviews { - if (!this._mainThreadWebviews) { - this._mainThreadWebviews = this._extensionService.getNamedCustomer('MainThreadWebviews'); - } - return this._mainThreadWebviews; - } - - private _onViewportChanged(): void { if (this._currentResolveCodeInsetSymbolsPromise) { this._currentResolveCodeInsetSymbolsPromise.cancel(); @@ -311,13 +308,18 @@ export class CodeInsetController implements editorCommon.IEditorContribution { const allPromises = allWidgetRequests.map((widgetRequests, r) => { const widgetPromises = widgetRequests.map(request => { - const symbol = request.symbol; - if (!symbol.webviewHandle && typeof request.provider.resolveCodeInset === 'function') { - const mainThreadWebviews = this.getWebviewService(); - symbol.webviewHandle = insetWidgets[r].createWebview(mainThreadWebviews, request.provider.extensionLocation); - return request.provider.resolveCodeInset(model, symbol, token); + if (request.resolved) { + return Promise.resolve(void 0); } - return Promise.resolve(void 0); + let a = new Promise(resolve => { + this._pendingWebviews.set(request.symbol.id, element => { + request.resolved = true; + insetWidgets[r].adoptWebview(element); + resolve(); + }); + }); + let b = request.provider.resolveCodeInset(model, request.symbol, token); + return Promise.all([a, b]); }); return Promise.all(widgetPromises); diff --git a/src/vs/workbench/contrib/codeinset/codeInset.ts b/src/vs/workbench/contrib/codeinset/codeInset.ts index 2301776cb185d..36a8ab933e4af 100644 --- a/src/vs/workbench/contrib/codeinset/codeInset.ts +++ b/src/vs/workbench/contrib/codeinset/codeInset.ts @@ -16,17 +16,16 @@ import { ProviderResult } from 'vs/editor/common/modes'; import { IRange } from 'vs/editor/common/core/range'; export interface ICodeInsetSymbol { + id: string; range: IRange; - id?: string; height?: number; - webviewHandle?: string; } export interface CodeInsetProvider { onDidChange?: Event; extensionLocation: UriComponents; provideCodeInsets(model: ITextModel, token: CancellationToken): ProviderResult; - resolveCodeInset?(model: ITextModel, codeInset: ICodeInsetSymbol, token: CancellationToken): ProviderResult; + resolveCodeInset(model: ITextModel, codeInset: ICodeInsetSymbol, token: CancellationToken): ProviderResult; } export const CodeInsetProviderRegistry = new LanguageFeatureRegistry(); @@ -34,6 +33,7 @@ export const CodeInsetProviderRegistry = new LanguageFeatureRegistry { diff --git a/src/vs/workbench/contrib/codeinset/codeInsetWidget.ts b/src/vs/workbench/contrib/codeinset/codeInsetWidget.ts index dca58609fdc29..12562860a9a33 100644 --- a/src/vs/workbench/contrib/codeinset/codeInsetWidget.ts +++ b/src/vs/workbench/contrib/codeinset/codeInsetWidget.ts @@ -4,16 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./codeInsetWidget'; -import { ICommandService } from 'vs/platform/commands/common/commands'; import { Range } from 'vs/editor/common/core/range'; import * as editorBrowser from 'vs/editor/browser/editorBrowser'; import { ICodeInsetData } from './codeInset'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { IModelDeltaDecoration, IModelDecorationsChangeAccessor, ITextModel } from 'vs/editor/common/model'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import { WebviewEditorInput } from 'vs/workbench/contrib/webview/electron-browser/webviewEditorInput'; -import { MainThreadWebviews } from 'vs/workbench/api/electron-browser/mainThreadWebview'; -import { UriComponents } from 'vs/base/common/uri'; +import { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/webviewElement'; export interface IDecorationIdCallback { @@ -52,21 +48,17 @@ export class CodeInsetHelper { export class CodeInsetWidget { private readonly _editor: editorBrowser.ICodeEditor; + private _webview: WebviewElement; private _viewZone: editorBrowser.IViewZone; private _viewZoneId?: number = undefined; private _decorationIds: string[]; private _data: ICodeInsetData[]; - private _webview: WebviewEditorInput | undefined; - private _webviewHandle: string | undefined; private _range: Range; constructor( data: ICodeInsetData[], // all the insets on the same line (often just one) editor: editorBrowser.ICodeEditor, - helper: CodeInsetHelper, - commandService: ICommandService, - notificationService: INotificationService, - updateCallabck: Function + helper: CodeInsetHelper ) { this._editor = editor; this._data = data; @@ -89,7 +81,6 @@ export class CodeInsetWidget { } public dispose(helper: CodeInsetHelper, viewZoneChangeAccessor: editorBrowser.IViewZoneChangeAccessor): void { - console.log('DISPOSE'); while (this._decorationIds.length) { helper.removeDecoration(this._decorationIds.pop()); } @@ -97,6 +88,9 @@ export class CodeInsetWidget { viewZoneChangeAccessor.removeZone(this._viewZoneId); this._viewZone = undefined; } + if (this._webview) { + this._webview.dispose(); + } } public isValid(): boolean { @@ -140,25 +134,17 @@ export class CodeInsetWidget { return -1; } - public get webview() { return this._webview; } - - static webviewPool = 1; - - public createWebview(mainThreadWebviews: MainThreadWebviews, extensionLocation: UriComponents) { - if (this._webviewHandle) { return this._webviewHandle; } + public adoptWebview(webview: WebviewElement): void { const lineNumber = this._range.endLineNumber; this._editor.changeViewZones(accessor => { + if (this._viewZoneId) { - this._webview.dispose(); accessor.removeZone(this._viewZoneId); + this._webview.dispose(); } - const div = document.createElement('div'); - - this._webviewHandle = CodeInsetWidget.webviewPool++ + ''; - this._webview = mainThreadWebviews.createInsetWebview(this._webviewHandle, div, { enableScripts: true }, extensionLocation); - const webview = this._webview.webview; + const div = document.createElement('div'); webview.mountTo(div); webview.onMessage((e: { type: string, payload: any }) => { // The webview contents can use a "size-info" message to report its size. @@ -170,15 +156,14 @@ export class CodeInsetWidget { }); } }); - this._viewZone = { afterLineNumber: lineNumber, heightInPx: 50, domNode: div }; this._viewZoneId = accessor.addZone(this._viewZone); + this._webview = webview; }); - return this._webviewHandle; } public reposition(viewZoneChangeAccessor: editorBrowser.IViewZoneChangeAccessor): void { diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewEditorService.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewEditorService.ts index 3bc8633774809..bbe24ca44d05a 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewEditorService.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewEditorService.ts @@ -12,8 +12,6 @@ import * as vscode from 'vscode'; import { WebviewEditorInput } from './webviewEditorInput'; import { GroupIdentifier } from 'vs/workbench/common/editor'; import { equals } from 'vs/base/common/arrays'; -import { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/webviewElement'; -import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; export const IWebviewEditorService = createDecorator('webviewEditorService'); @@ -34,13 +32,6 @@ export interface IWebviewEditorService { events: WebviewEvents ): WebviewEditorInput; - createInsetWebview( - parent: HTMLElement, - options: vscode.WebviewOptions, - extensionLocation: URI, - events: WebviewEvents - ): WebviewEditorInput; - reviveWebview( viewType: string, id: number, @@ -105,8 +96,7 @@ export class WebviewEditorService implements IWebviewEditorService { constructor( @IEditorService private readonly _editorService: IEditorService, @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, - @IPartService private readonly _partService: IPartService, + @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService ) { } createWebview( @@ -122,21 +112,6 @@ export class WebviewEditorService implements IWebviewEditorService { return webviewInput; } - createInsetWebview( - parent: HTMLElement, - options: vscode.WebviewOptions, - extensionLocation: URI, - events: WebviewEvents - ): WebviewEditorInput { - const webviewEditorInput = this._instantiationService.createInstance(WebviewEditorInput, 'codeinset', undefined, '', options, {}, events, extensionLocation, undefined); - webviewEditorInput.webview = this._instantiationService.createInstance(WebviewElement, - this._partService.getContainer(Parts.EDITOR_PART), - { allowSvgs: true, useSameOriginForRoot: true }, - { allowScripts: true, disableFindView: true }); - webviewEditorInput.webview.mountTo(parent); - return webviewEditorInput; - } - revealWebview( webview: WebviewEditorInput, group: IEditorGroup, diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index 247fcde06fffe..bab03582819d5 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -222,9 +222,6 @@ export interface IExtensionService extends ICpuProfilerTarget { * Stops the extension host. */ stopExtensionHost(): void; - - getNamedCustomer?(sid: string): any; - } export interface ICpuProfilerTarget { @@ -285,4 +282,4 @@ export class NullExtensionService implements IExtensionService { stopExtensionHost(): void { } canAddExtension(): boolean { return false; } canRemoveExtension(): boolean { return false; } -} \ No newline at end of file +} diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 2dd7b5fe86b67..d7d69bb07c911 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -417,11 +417,6 @@ export class ExtensionService extends Disposable implements IExtensionService { this._stopExtensionHostProcess(); } - public getNamedCustomer(sid: string): any { - const ncs = this._extensionHostProcessManagers.map(m => m.getNamedCustomer(sid)).filter(c => c); - return ncs.length ? ncs[0] : undefined; - } - private _stopExtensionHostProcess(): void { let previouslyActivatedExtensionIds: ExtensionIdentifier[] = []; this._extensionHostActiveExtensions.forEach((value) => { From a5f1ac75261740dd494f994884827172c1aea69c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 13 Feb 2019 12:55:09 +0100 Subject: [PATCH 10/14] reduce amount of changes --- .../webview/electron-browser/webviewEditorService.ts | 2 +- .../contrib/webview/electron-browser/webviewElement.ts | 10 +--------- .../workbench/services/extensions/common/extensions.ts | 2 +- .../electron-browser/extensionHostProcessManager.ts | 6 ------ 4 files changed, 3 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewEditorService.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewEditorService.ts index bbe24ca44d05a..10dbe724d1726 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewEditorService.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewEditorService.ts @@ -96,7 +96,7 @@ export class WebviewEditorService implements IWebviewEditorService { constructor( @IEditorService private readonly _editorService: IEditorService, @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService + @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, ) { } createWebview( diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index cc5a97d889889..62ec513e1d5b3 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -373,7 +373,7 @@ export class WebviewElement extends Disposable { public mountTo(parent: HTMLElement) { if (this._webviewFindWidget) { - parent.appendChild(this._webviewFindWidget.getDomNode()); + parent.appendChild(this._webviewFindWidget.getDomNode()!); } parent.appendChild(this._webview); } @@ -402,9 +402,6 @@ export class WebviewElement extends Disposable { private readonly _onMessage = this._register(new Emitter()); public readonly onMessage = this._onMessage.event; - private readonly _onLayout = this._register(new Emitter<{ width: number, height: number }>()); - public readonly onLayout = this._onLayout.event; - private _send(channel: string, ...args: any[]): void { this._ready .then(() => this._webview.send(channel, ...args)) @@ -525,11 +522,6 @@ export class WebviewElement extends Disposable { } contents.setZoomFactor(factor); - if (!this._webview || !this._webview.parentElement) { - return; - } - - this._onLayout.fire({ width: this._webview.clientWidth, height: this._webview.clientHeight }); }); } diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index bab03582819d5..d40c975542cf9 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -282,4 +282,4 @@ export class NullExtensionService implements IExtensionService { stopExtensionHost(): void { } canAddExtension(): boolean { return false; } canRemoveExtension(): boolean { return false; } -} +} \ No newline at end of file diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts index f3a9fe81a7794..277ad1cbd429e 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts @@ -48,7 +48,6 @@ export class ExtensionHostProcessManager extends Disposable { private _extensionHostProcessRPCProtocol: RPCProtocol; private readonly _extensionHostProcessCustomers: IDisposable[]; private readonly _extensionHostProcessWorker: IExtensionHostStarter; - private readonly _namedCustomerById: { [sid: string]: any } = {}; /** * winjs believes a proxy is a promise because it has a `then` method, so wrap the result in an object. */ @@ -184,7 +183,6 @@ export class ExtensionHostProcessManager extends Disposable { const instance = this._instantiationService.createInstance(ctor, extHostContext); this._extensionHostProcessCustomers.push(instance); this._extensionHostProcessRPCProtocol.set(id, instance); - this._namedCustomerById[id.sid] = instance; } // Customers @@ -207,10 +205,6 @@ export class ExtensionHostProcessManager extends Disposable { }); } - public getNamedCustomer(sid: string): any { - return this._namedCustomerById[sid]; - } - public activateByEvent(activationEvent: string): Promise { if (this._extensionHostProcessFinishedActivateEvents[activationEvent] || !this._extensionHostProcessProxy) { return NO_OP_VOID_PROMISE; From 302533f60511bbf823aafdade2548cf2869d60eb Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 13 Feb 2019 12:58:25 +0100 Subject: [PATCH 11/14] strict null trouble --- src/vs/workbench/contrib/codeinset/codeInset.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/codeinset/codeInset.ts b/src/vs/workbench/contrib/codeinset/codeInset.ts index 36a8ab933e4af..a9402e36690f4 100644 --- a/src/vs/workbench/contrib/codeinset/codeInset.ts +++ b/src/vs/workbench/contrib/codeinset/codeInset.ts @@ -94,7 +94,7 @@ registerLanguageCommand('_executeCodeInsetProvider', function (accessor, args) { result.push(item.symbol); } else if (itemResolveCount-- > 0) { resolve.push(Promise.resolve(item.provider.resolveCodeInset(model, item.symbol, CancellationToken.None)) - .then(symbol => result.push(symbol))); + .then(symbol => result.push(symbol || item.symbol))); } } From 58d2b5be0678ab578da31bb182f6321c54d54e52 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 13 Feb 2019 14:46:31 +0100 Subject: [PATCH 12/14] disable event for now, remove extension location from provider --- .../electron-browser/mainThreadLanguageFeatures.ts | 6 ++---- src/vs/workbench/api/node/extHost.protocol.ts | 2 +- src/vs/workbench/api/node/extHostLanguageFeatures.ts | 12 ++++++------ src/vs/workbench/contrib/codeinset/codeInset.ts | 3 +-- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts index 1aa547ec9ad24..20854ecf66bc9 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts @@ -20,7 +20,6 @@ import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostC import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters'; import { URI } from 'vs/base/common/uri'; import { Selection } from 'vs/editor/common/core/selection'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import * as codeInset from 'vs/workbench/contrib/codeinset/codeInset'; @extHostNamedCustomer(MainContext.MainThreadLanguageFeatures) @@ -164,7 +163,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha // -- code inset - $registerCodeInsetSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number, extension: IExtensionDescription): void { + $registerCodeInsetSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number): void { const provider = { provideCodeInsets: (model: ITextModel, token: CancellationToken): CodeInsetDto[] | Thenable => { @@ -178,8 +177,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha this._heapService.trackObject(obj); return obj; }); - }, - extensionLocation: extension.extensionLocation + } }; if (typeof eventHandle === 'number') { diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index dadca886aa0b1..4dad35ff4dec7 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -300,7 +300,7 @@ 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, extension: IExtensionDescription): void; + $registerCodeInsetSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number): void; $emitCodeLensEvent(eventHandle: number, event?: any): void; $registerDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void; $registerDeclarationSupport(handle: number, selector: ISerializedDocumentFilter[]): void; diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index 15a14ef8e6e6f..ecd56ea3c18f6 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -1134,16 +1134,16 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { registerCodeInsetProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CodeInsetProvider): vscode.Disposable { const handle = this._nextHandle(); - const eventHandle = typeof provider.onDidChangeCodeInsets === 'function' ? this._nextHandle() : undefined; + // 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, extension); + this._proxy.$registerCodeInsetSupport(handle, this._transformDocumentSelector(selector), undefined); let result = this._createDisposable(handle); - if (eventHandle !== undefined) { - const subscription = provider.onDidChangeCodeInsets(_ => this._proxy.$emitCodeLensEvent(eventHandle)); - result = Disposable.from(result, subscription); - } + // if (eventHandle !== undefined) { + // const subscription = provider.onDidChangeCodeInsets(_ => this._proxy.$emitCodeLensEvent(eventHandle)); + // result = Disposable.from(result, subscription); + // } return result; } diff --git a/src/vs/workbench/contrib/codeinset/codeInset.ts b/src/vs/workbench/contrib/codeinset/codeInset.ts index a9402e36690f4..c04b07c72c0f7 100644 --- a/src/vs/workbench/contrib/codeinset/codeInset.ts +++ b/src/vs/workbench/contrib/codeinset/codeInset.ts @@ -7,7 +7,7 @@ import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions'; import { ITextModel } from 'vs/editor/common/model'; import { onUnexpectedExternalError, illegalArgument } from 'vs/base/common/errors'; import { mergeSort } from 'vs/base/common/arrays'; -import { URI, UriComponents } from 'vs/base/common/uri'; +import { URI } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; import { IModelService } from 'vs/editor/common/services/modelService'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -23,7 +23,6 @@ export interface ICodeInsetSymbol { export interface CodeInsetProvider { onDidChange?: Event; - extensionLocation: UriComponents; provideCodeInsets(model: ITextModel, token: CancellationToken): ProviderResult; resolveCodeInset(model: ITextModel, codeInset: ICodeInsetSymbol, token: CancellationToken): ProviderResult; } From b49bf46bf21f4749c5592d9943762c732cea1b9a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 13 Feb 2019 14:51:32 +0100 Subject: [PATCH 13/14] remove api command for code insets --- .../workbench/api/node/extHostApiCommands.ts | 16 -------- .../workbench/contrib/codeinset/codeInset.ts | 37 +------------------ 2 files changed, 1 insertion(+), 52 deletions(-) diff --git a/src/vs/workbench/api/node/extHostApiCommands.ts b/src/vs/workbench/api/node/extHostApiCommands.ts index a47fd3c72f68c..ef757ca94f004 100644 --- a/src/vs/workbench/api/node/extHostApiCommands.ts +++ b/src/vs/workbench/api/node/extHostApiCommands.ts @@ -18,7 +18,6 @@ import { CustomCodeAction } from 'vs/workbench/api/node/extHostLanguageFeatures' import { ICommandsExecutor, PreviewHTMLAPICommand, OpenFolderAPICommand, DiffAPICommand, OpenAPICommand, RemoveFromRecentlyOpenedAPICommand, SetEditorLayoutAPICommand } from './apiCommands'; import { EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService'; import { isFalsyOrEmpty } from 'vs/base/common/arrays'; -import * as codeInset from 'vs/workbench/contrib/codeinset/codeInset'; export class ExtHostApiCommands { @@ -147,13 +146,6 @@ export class ExtHostApiCommands { ], returns: 'A promise that resolves to an array of CodeLens-instances.' }); - this._register('vscode.executeCodeInsetProvider', this._executeCodeInsetProvider, { - description: 'Execute CodeInset provider.', - args: [ - { name: 'uri', description: 'Uri of a text document', constraint: URI } - ], - returns: 'A promise that resolves to an array of CodeInset-instances.' - }); this._register('vscode.executeFormatDocumentProvider', this._executeFormatDocumentProvider, { description: 'Execute document format provider.', args: [ @@ -522,14 +514,6 @@ export class ExtHostApiCommands { } - private _executeCodeInsetProvider(resource: URI): Thenable { - const args = { resource }; - return this._commands.executeCommand('_executeCodeInsetProvider', args) - .then(tryMapWith(item => - new types.CodeInset( - typeConverters.Range.to(item.range)))); - } - private _executeFormatDocumentProvider(resource: URI, options: vscode.FormattingOptions): Promise { const args = { resource, diff --git a/src/vs/workbench/contrib/codeinset/codeInset.ts b/src/vs/workbench/contrib/codeinset/codeInset.ts index c04b07c72c0f7..cb37c59d8f9d6 100644 --- a/src/vs/workbench/contrib/codeinset/codeInset.ts +++ b/src/vs/workbench/contrib/codeinset/codeInset.ts @@ -3,13 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions'; import { ITextModel } from 'vs/editor/common/model'; -import { onUnexpectedExternalError, illegalArgument } from 'vs/base/common/errors'; +import { onUnexpectedExternalError } from 'vs/base/common/errors'; import { mergeSort } from 'vs/base/common/arrays'; -import { URI } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; -import { IModelService } from 'vs/editor/common/services/modelService'; import { CancellationToken } from 'vs/base/common/cancellation'; import { LanguageFeatureRegistry } from 'vs/editor/common/modes/languageFeatureRegistry'; import { ProviderResult } from 'vs/editor/common/modes'; @@ -71,35 +68,3 @@ export function getCodeInsetData(model: ITextModel, token: CancellationToken): P }); }); } - -registerLanguageCommand('_executeCodeInsetProvider', function (accessor, args) { - let { resource, itemResolveCount } = args; - if (!(resource instanceof URI)) { - throw illegalArgument(); - } - - const model = accessor.get(IModelService).getModel(resource); - if (!model) { - throw illegalArgument(); - } - - const result: ICodeInsetSymbol[] = []; - return getCodeInsetData(model, CancellationToken.None).then(value => { - - let resolve: Thenable[] = []; - - for (const item of value) { - if (typeof itemResolveCount === 'undefined') { - result.push(item.symbol); - } else if (itemResolveCount-- > 0) { - resolve.push(Promise.resolve(item.provider.resolveCodeInset(model, item.symbol, CancellationToken.None)) - .then(symbol => result.push(symbol || item.symbol))); - } - } - - return Promise.all(resolve); - - }).then(() => { - return result; - }); -}); From ac03e62afcd45cf13e76af0d36a6f85f46f3c2f0 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 13 Feb 2019 15:20:29 +0100 Subject: [PATCH 14/14] last round of making changes smaller --- src/vs/vscode.proposed.d.ts | 2 +- .../api/electron-browser/mainThreadWebview.ts | 4 +--- src/vs/workbench/api/node/extHost.protocol.ts | 13 ------------ .../api/node/extHostLanguageFeatures.ts | 12 +++++------ src/vs/workbench/api/node/extHostTypes.ts | 2 -- .../webview/electron-browser/webview-pre.js | 2 +- .../electron-browser/webviewElement.ts | 21 +++---------------- 7 files changed, 12 insertions(+), 44 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 2cfff0cea1a57..9159859427b60 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -33,8 +33,8 @@ declare module 'vscode' { export namespace workspace { export function registerRemoteAuthorityResolver(authorityPrefix: string, resolver: RemoteAuthorityResolver): Disposable; - } + //#endregion diff --git a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts index 03b880ecec225..2c740a314d4fb 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts @@ -30,8 +30,6 @@ import { IPartService, Parts } from 'vs/workbench/services/part/common/partServi @extHostNamedCustomer(MainContext.MainThreadWebviews) export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviver { - _serviceBrand: any; - private static readonly viewType = 'mainThreadWebview'; private static readonly standardSupportedLinkSchemes = ['http', 'https', 'mailto']; @@ -352,7 +350,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv } } - public getWebview(handle: WebviewPanelHandle): WebviewEditorInput { + private getWebview(handle: WebviewPanelHandle): WebviewEditorInput { const webview = this._webviews.get(handle); if (!webview) { throw new Error('Unknown webview handle:' + handle); diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 4dad35ff4dec7..68596ed1466ee 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -505,18 +505,6 @@ export interface ExtHostWebviewsShape { $deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: vscode.WebviewOptions): Promise; } -// export type CodeInsetWebviewHandle = string; - -// export interface ExtHostCodeInsetWebviewsShape { -// } - -// export interface ExtHostCodeInsetWebviewShape extends IDisposable { -// $setHtml(handle: WebviewPanelHandle, value: string): void; -// $setOptions(handle: WebviewPanelHandle, options: vscode.WebviewOptions): void; -// $postMessage(handle: WebviewPanelHandle, value: any): Promise; -// } - - export interface MainThreadUrlsShape extends IDisposable { $registerUriHandler(handle: number, extensionId: ExtensionIdentifier): Promise; $unregisterUriHandler(handle: number): Promise; @@ -1179,7 +1167,6 @@ export const ExtHostContext = { ExtHostWorkspace: createExtId('ExtHostWorkspace'), ExtHostWindow: createExtId('ExtHostWindow'), ExtHostWebviews: createExtId('ExtHostWebviews'), - // ExtHostCodeInsetWebviews: createExtId('ExtHostWebviews'), ExtHostProgress: createMainId('ExtHostProgress'), ExtHostComments: createMainId('ExtHostComments'), ExtHostStorage: createMainId('ExtHostStorage'), diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index ecd56ea3c18f6..5afbf25903db7 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -1134,16 +1134,16 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { registerCodeInsetProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CodeInsetProvider): vscode.Disposable { const handle = this._nextHandle(); - // const eventHandle = typeof provider.onDidChangeCodeInsets === 'function' ? this._nextHandle() : undefined; + 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), undefined); + this._proxy.$registerCodeInsetSupport(handle, this._transformDocumentSelector(selector), eventHandle); let result = this._createDisposable(handle); - // if (eventHandle !== undefined) { - // const subscription = provider.onDidChangeCodeInsets(_ => this._proxy.$emitCodeLensEvent(eventHandle)); - // result = Disposable.from(result, subscription); - // } + if (eventHandle !== undefined) { + const subscription = provider.onDidChangeCodeInsets(_ => this._proxy.$emitCodeLensEvent(eventHandle)); + result = Disposable.from(result, subscription); + } return result; } diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index f70a2acbe2b00..625ba1018ee82 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -1140,12 +1140,10 @@ export class CodeLens { export class CodeInset { range: Range; - isResolved: boolean; height?: number; constructor(range: Range, height?: number) { this.range = range; - this.isResolved = false; this.height = height; } } diff --git a/src/vs/workbench/contrib/webview/electron-browser/webview-pre.js b/src/vs/workbench/contrib/webview/electron-browser/webview-pre.js index 36d3759320d22..5fb945732ccda 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webview-pre.js +++ b/src/vs/workbench/contrib/webview/electron-browser/webview-pre.js @@ -243,7 +243,7 @@ delete window.frameElement; `; - newDocument.head.prepend(defaultScript); + newDocument.head.prepend(defaultScript, newDocument.head.firstChild); } // apply default styles diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index 62ec513e1d5b3..4f2604715a045 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -13,12 +13,12 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import * as colorRegistry from 'vs/platform/theme/common/colorRegistry'; import { DARK, ITheme, IThemeService, LIGHT } from 'vs/platform/theme/common/themeService'; import { registerFileProtocol, WebviewProtocol } from 'vs/workbench/contrib/webview/electron-browser/webviewProtocols'; +import { areWebviewInputOptionsEqual } from './webviewEditorService'; import { WebviewFindWidget } from './webviewFindWidget'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { endsWith } from 'vs/base/common/strings'; import { isMacintosh } from 'vs/base/common/platform'; -import { equals } from 'vs/base/common/arrays'; export interface WebviewOptions { readonly allowSvgs?: boolean; @@ -33,20 +33,6 @@ export interface WebviewContentOptions { readonly disableFindView?: boolean; } - -export function areWebviewContentOptionsEqual(a: WebviewContentOptions, b: WebviewContentOptions): boolean { - const sameArray = (a1: ReadonlyArray, a2: ReadonlyArray) => - (a.localResourceRoots === b.localResourceRoots - || (Array.isArray(a.localResourceRoots) && Array.isArray(b.localResourceRoots) - && equals(a.localResourceRoots, b.localResourceRoots, (a, b) => a.toString() === b.toString()))); - - return a.allowScripts === b.allowScripts - && a.disableFindView === b.disableFindView - && sameArray(a.svgWhiteList, b.svgWhiteList) - && sameArray(a.localResourceRoots, b.localResourceRoots); -} - - interface IKeydownEvent { key: string; keyCode: number; @@ -263,7 +249,6 @@ export class WebviewElement extends Disposable { this._webview.setAttribute('partition', `webview${Date.now()}`); this._webview.setAttribute('webpreferences', 'contextIsolation=yes'); - this._webview.setAttribute('autosize', 'on'); this._webview.style.flex = '0 1'; this._webview.style.width = '0'; @@ -417,7 +402,7 @@ export class WebviewElement extends Disposable { } public set options(value: WebviewContentOptions) { - if (this._contentOptions && areWebviewContentOptionsEqual(value, this._contentOptions)) { + if (this._contentOptions && areWebviewInputOptionsEqual(value, this._contentOptions)) { return; } @@ -439,7 +424,7 @@ export class WebviewElement extends Disposable { } public update(value: string, options: WebviewContentOptions, retainContextWhenHidden: boolean) { - if (retainContextWhenHidden && value === this._contents && this._contentOptions && areWebviewContentOptionsEqual(options, this._contentOptions)) { + if (retainContextWhenHidden && value === this._contents && this._contentOptions && areWebviewInputOptionsEqual(options, this._contentOptions)) { return; } this._contents = value;