Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code inset feature #66418

Merged
merged 14 commits into from Feb 13, 2019
Merged
28 changes: 28 additions & 0 deletions src/vs/vscode.proposed.d.ts
Expand Up @@ -38,6 +38,34 @@ declare module 'vscode' {
//#endregion


// #region Joh - code insets

/**
*/
export class CodeInset {
range: Range;
height?: number;
constructor(range: Range, height?: number);
}

export interface CodeInsetProvider {
onDidChangeCodeInsets?: Event<void>;
provideCodeInsets(document: TextDocument, token: CancellationToken): ProviderResult<CodeInset[]>;
resolveCodeInset(codeInset: CodeInset, webview: Webview, token: CancellationToken): ProviderResult<CodeInset>;
}

export namespace languages {

/**
* Register a code inset provider.
*
*/
export function registerCodeInsetProvider(selector: DocumentSelector, provider: CodeInsetProvider): Disposable;
}

//#endregion


//#region Joh - selection range provider

export class SelectionRangeKind {
Expand Down
Expand Up @@ -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';
Expand All @@ -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 * as codeInset from 'vs/workbench/contrib/codeinset/codeInset';

@extHostNamedCustomer(MainContext.MainThreadLanguageFeatures)
export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesShape {
Expand Down Expand Up @@ -160,6 +161,35 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
}
}

// -- code inset

$registerCodeInsetSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number): void {

const provider = <codeInset.CodeInsetProvider>{
provideCodeInsets: (model: ITextModel, token: CancellationToken): CodeInsetDto[] | Thenable<CodeInsetDto[]> => {
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<CodeInsetDto> => {
return this._proxy.$resolveCodeInset(handle, model.uri, codeInset, token).then(obj => {
this._heapService.trackObject(obj);
return obj;
});
}
};

if (typeof eventHandle === 'number') {
const emitter = new Emitter<codeInset.CodeInsetProvider>();
this._registrations[eventHandle] = emitter;
provider.onDidChange = emitter.event;
}

const langSelector = typeConverters.LanguageSelector.from(selector);
this._registrations[handle] = codeInset.CodeInsetProviderRegistry.register(langSelector, provider);
}

// --- declaration

$registerDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void {
Expand Down
97 changes: 79 additions & 18 deletions src/vs/workbench/api/electron-browser/mainThreadWebview.ts
Expand Up @@ -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';
Expand All @@ -21,6 +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 {
Expand All @@ -35,6 +40,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv

private readonly _proxy: ExtHostWebviewsShape;
private readonly _webviews = new Map<WebviewPanelHandle, WebviewEditorInput>();
private readonly _webviewsElements = new Map<WebviewInsetHandle, WebviewElement>();
private readonly _revivers = new Set<string>();

private _activeWebview: WebviewPanelHandle | undefined = undefined;
Expand All @@ -47,7 +53,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);
Expand Down Expand Up @@ -96,6 +105,44 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv
this._telemetryService.publicLog('webviews:createWebviewPanel', { extensionId: extensionId.value });
}

$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;
}
}

if (!found) {
webview.dispose();
return;
}
// this will leak... the adopted webview will be disposed by the
// code inset controller. we might need a dispose-event here so that
// we can clean up things.
this._webviewsElements.set(handle, webview);
}

public $disposeWebview(handle: WebviewPanelHandle): void {
const webview = this.getWebview(handle);
webview.dispose();
Expand All @@ -111,14 +158,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 {
Expand All @@ -132,18 +187,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<boolean> {
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<boolean> {
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 {
Expand Down
7 changes: 6 additions & 1 deletion src/vs/workbench/api/node/extHost.api.impl.ts
Expand Up @@ -304,6 +304,10 @@ export function createApiFactory(
registerCodeLensProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable {
return extHostLanguageFeatures.registerCodeLensProvider(extension, checkSelector(selector), provider);
},
registerCodeInsetProvider(selector: vscode.DocumentSelector, provider: vscode.CodeInsetProvider): vscode.Disposable {
checkProposedApiEnabled(extension);
return extHostLanguageFeatures.registerCodeInsetProvider(extension, checkSelector(selector), provider);
},
registerDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable {
return extHostLanguageFeatures.registerDefinitionProvider(extension, checkSelector(selector), provider);
},
Expand Down Expand Up @@ -474,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') {
Expand Down Expand Up @@ -754,6 +758,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,
Expand Down
16 changes: 13 additions & 3 deletions src/vs/workbench/api/node/extHost.protocol.ts
Expand Up @@ -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/contrib/codeinset/codeInset';

export interface IEnvironment {
isExtensionDevelopmentDebug: boolean;
Expand Down Expand Up @@ -299,6 +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): void;
$emitCodeLensEvent(eventHandle: number, event?: any): void;
$registerDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerDeclarationSupport(handle: number, selector: ISerializedDocumentFilter[]): void;
Expand Down Expand Up @@ -467,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 {
$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<boolean>;

$setHtml(handle: WebviewPanelHandle | WebviewInsetHandle, value: string): void;
$setOptions(handle: WebviewPanelHandle | WebviewInsetHandle, options: vscode.WebviewOptions): void;
$postMessage(handle: WebviewPanelHandle | WebviewInsetHandle, value: any): Promise<boolean>;

$registerSerializer(viewType: string): void;
$unregisterSerializer(viewType: string): void;
Expand Down Expand Up @@ -892,10 +898,14 @@ export interface CodeLensDto extends ObjectIdentifier {
command?: CommandDto;
}

export type CodeInsetDto = ObjectIdentifier & codeInset.ICodeInsetSymbol;

export interface ExtHostLanguageFeaturesShape {
$provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise<modes.DocumentSymbol[] | undefined>;
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<CodeLensDto[]>;
$resolveCodeLens(handle: number, resource: UriComponents, symbol: CodeLensDto, token: CancellationToken): Promise<CodeLensDto>;
$provideCodeInsets(handle: number, resource: UriComponents, token: CancellationToken): Promise<CodeInsetDto[]>;
$resolveCodeInset(handle: number, resource: UriComponents, symbol: CodeInsetDto, token: CancellationToken): Promise<CodeInsetDto>;
$provideDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<DefinitionLinkDto[]>;
$provideDeclaration(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<DefinitionLinkDto[]>;
$provideImplementation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<DefinitionLinkDto[]>;
Expand Down