Add support for clickable links and other webview options (#2396)

* Add support for clickable links and other webview options
- Added click handling
- Added suport for localResourceRoots to be read in the webview

* Options should not be mandatory

* Ensure the constructor-defined properties are preserved
- This fixes issue where the extensionFolderPath was lost during webview withProperties call in the modelbuilder.

* enableCommandUris should be a getter

* Add position support to webview and to flexContainer

* Fix regressions on editor view caused by merge
This commit is contained in:
Kevin Cunnane
2018-09-05 13:28:22 -07:00
committed by GitHub
parent 05cf06656d
commit ac96919caf
12 changed files with 184 additions and 73 deletions

View File

@@ -139,6 +139,14 @@ export abstract class ComponentBase extends Disposable implements IComponent, On
this.setPropertyFromUI<sqlops.ComponentProperties, number | string>((props, value) => props.width = value, newValue); this.setPropertyFromUI<sqlops.ComponentProperties, number | string>((props, value) => props.width = value, newValue);
} }
public get position(): string {
return this.getPropertyOrDefault<sqlops.EditorProperties, string>((props) => props.position, '');
}
public set position(newValue: string) {
this.setPropertyFromUI<sqlops.EditorProperties, string>((properties, position) => { properties.position = position; }, newValue);
}
public convertSizeToNumber(size: number | string): number { public convertSizeToNumber(size: number | string): number {
if (size && typeof (size) === 'string') { if (size && typeof (size) === 'string') {
if (size.toLowerCase().endsWith('px')) { if (size.toLowerCase().endsWith('px')) {

View File

@@ -21,6 +21,9 @@ import { IModelService } from 'vs/editor/common/services/modelService';
import { ComponentBase } from 'sql/parts/modelComponents/componentBase'; import { ComponentBase } from 'sql/parts/modelComponents/componentBase';
import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces'; import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
import { QueryTextEditor } from 'sql/parts/modelComponents/queryTextEditor'; import { QueryTextEditor } from 'sql/parts/modelComponents/queryTextEditor';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { SimpleProgressService } from 'vs/editor/standalone/browser/simpleServices';
import { IProgressService } from 'vs/platform/progress/common/progress';
@Component({ @Component({
template: '', template: '',
@@ -55,11 +58,12 @@ export default class EditorComponent extends ComponentBase implements IComponent
} }
private _createEditor(): void { private _createEditor(): void {
this._editor = this._instantiationService.createInstance(QueryTextEditor); let instantiationService = this._instantiationService.createChild(new ServiceCollection([IProgressService, new SimpleProgressService()]));
this._editor = instantiationService.createInstance(QueryTextEditor);
this._editor.create(this._el.nativeElement); this._editor.create(this._el.nativeElement);
this._editor.setVisible(true); this._editor.setVisible(true);
let uri = this.createUri(); let uri = this.createUri();
this._editorInput = this._instantiationService.createInstance(UntitledEditorInput, uri, false, 'sql', '', ''); this._editorInput = instantiationService.createInstance(UntitledEditorInput, uri, false, 'sql', '', '');
this._editor.setInput(this._editorInput, undefined); this._editor.setInput(this._editorInput, undefined);
this._editorInput.resolve().then(model => this._editorModel = model.textEditorModel); this._editorInput.resolve().then(model => this._editorModel = model.textEditorModel);
@@ -154,14 +158,6 @@ export default class EditorComponent extends ComponentBase implements IComponent
this.setPropertyFromUI<sqlops.EditorProperties, string>((properties, languageMode) => { properties.languageMode = languageMode; }, newValue); this.setPropertyFromUI<sqlops.EditorProperties, string>((properties, languageMode) => { properties.languageMode = languageMode; }, newValue);
} }
public get position(): string {
return this.getPropertyOrDefault<sqlops.EditorProperties, string>((props) => props.position, '');
}
public set position(newValue: string) {
this.setPropertyFromUI<sqlops.EditorProperties, string>((properties, position) => { properties.position = position; }, newValue);
}
public get editorUri(): string { public get editorUri(): string {
return this.getPropertyOrDefault<sqlops.EditorProperties, string>((props) => props.editorUri, ''); return this.getPropertyOrDefault<sqlops.EditorProperties, string>((props) => props.editorUri, '');
} }

View File

@@ -24,7 +24,7 @@ class FlexItem {
@Component({ @Component({
template: ` template: `
<div *ngIf="items" class="flexContainer" [style.flexFlow]="flexFlow" [style.justifyContent]="justifyContent" <div *ngIf="items" class="flexContainer" [style.flexFlow]="flexFlow" [style.justifyContent]="justifyContent" [style.position]="position"
[style.alignItems]="alignItems" [style.alignContent]="alignContent" [style.height]="height" [style.width]="width"> [style.alignItems]="alignItems" [style.alignContent]="alignContent" [style.height]="height" [style.width]="width">
<div *ngFor="let item of items" [style.flex]="getItemFlex(item)" [style.textAlign]="textAlign" [style.order]="getItemOrder(item)" [ngStyle]="getItemStyles(item)"> <div *ngFor="let item of items" [style.flex]="getItemFlex(item)" [style.textAlign]="textAlign" [style.order]="getItemOrder(item)" [ngStyle]="getItemStyles(item)">
<model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore"> <model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore">
@@ -43,6 +43,7 @@ export default class FlexContainer extends ContainerBase<FlexItemLayout> impleme
private _textAlign: string; private _textAlign: string;
private _height: string; private _height: string;
private _width: string; private _width: string;
private _position: string;
constructor(@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef) { constructor(@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef) {
super(changeRef); super(changeRef);
@@ -67,6 +68,7 @@ export default class FlexContainer extends ContainerBase<FlexItemLayout> impleme
this._alignItems = layout.alignItems ? layout.alignItems : ''; this._alignItems = layout.alignItems ? layout.alignItems : '';
this._alignContent = layout.alignContent ? layout.alignContent : ''; this._alignContent = layout.alignContent ? layout.alignContent : '';
this._textAlign = layout.textAlign ? layout.textAlign : ''; this._textAlign = layout.textAlign ? layout.textAlign : '';
this._position = layout.position ? layout.position : '';
this._height = this.convertSize(layout.height); this._height = this.convertSize(layout.height);
this._width = this.convertSize(layout.width); this._width = this.convertSize(layout.width);
@@ -102,6 +104,10 @@ export default class FlexContainer extends ContainerBase<FlexItemLayout> impleme
return this._textAlign; return this._textAlign;
} }
public get position(): string {
return this._position;
}
private getItemFlex(item: FlexItem): string { private getItemFlex(item: FlexItem): string {
return item.config ? item.config.flex : '1 1 auto'; return item.config ? item.config.flex : '1 1 auto';
} }

View File

@@ -7,7 +7,6 @@ import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import * as nls from 'vs/nls'; import * as nls from 'vs/nls';
import * as DOM from 'vs/base/browser/dom'; import * as DOM from 'vs/base/browser/dom';
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
import { TPromise } from 'vs/base/common/winjs.base';
import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel'; import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel';
import * as editorCommon from 'vs/editor/common/editorCommon'; import * as editorCommon from 'vs/editor/common/editorCommon';
@@ -21,22 +20,10 @@ import { IModeService } from 'vs/editor/common/services/modeService';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
import { EditorOptions } from 'vs/workbench/common/editor'; import { EditorOptions } from 'vs/workbench/common/editor';
import { IEditorContributionCtor } from 'vs/editor/browser/editorExtensions';
import { FoldingController } from 'vs/editor/contrib/folding/folding';
import { RenameController } from 'vs/editor/contrib/rename/rename';
import { StandaloneCodeEditor } from 'vs/editor/standalone/browser/standaloneCodeEditor'; import { StandaloneCodeEditor } from 'vs/editor/standalone/browser/standaloneCodeEditor';
import { CancellationToken } from 'vs/base/common/cancellation'; import { CancellationToken } from 'vs/base/common/cancellation';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
class QueryCodeEditor extends StandaloneCodeEditor {
// protected _getContributions(): IEditorContributionCtor[] {
// let contributions = super._getContributions();
// let skipContributions = [FoldingController.prototype, RenameController.prototype];
// contributions = contributions.filter(c => skipContributions.indexOf(c.prototype) === -1);
// return contributions;
// }
}
/** /**
* Extension of TextResourceEditor that is always readonly rather than only with non UntitledInputs * Extension of TextResourceEditor that is always readonly rather than only with non UntitledInputs
*/ */
@@ -62,7 +49,7 @@ export class QueryTextEditor extends BaseTextEditor {
} }
public createEditorControl(parent: HTMLElement, configuration: IEditorOptions): editorCommon.IEditor { public createEditorControl(parent: HTMLElement, configuration: IEditorOptions): editorCommon.IEditor {
return this.instantiationService.createInstance(QueryCodeEditor, parent, configuration); return this.instantiationService.createInstance(StandaloneCodeEditor, parent, configuration);
} }
protected getConfigurationOverrides(): IEditorOptions { protected getConfigurationOverrides(): IEditorOptions {

View File

@@ -9,14 +9,17 @@ import {
} from '@angular/core'; } from '@angular/core';
import * as sqlops from 'sqlops'; import * as sqlops from 'sqlops';
import { Event, Emitter } from 'vs/base/common/event'; import * as vscode from 'vscode';
import { addDisposableListener, EventType } from 'vs/base/browser/dom'; import { addDisposableListener, EventType } from 'vs/base/browser/dom';
import { Parts, IPartService } from 'vs/workbench/services/part/common/partService'; import { Parts, IPartService } from 'vs/workbench/services/part/common/partService';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service'; import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IThemeService } from 'vs/platform/theme/common/themeService';
import { WebviewElement } from 'vs/workbench/parts/webview/electron-browser/webviewElement'; import { WebviewElement, WebviewOptions } from 'vs/workbench/parts/webview/electron-browser/webviewElement';
import URI, { UriComponents } from 'vs/base/common/uri';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { ComponentBase } from 'sql/parts/modelComponents/componentBase'; import { ComponentBase } from 'sql/parts/modelComponents/componentBase';
import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces'; import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
@@ -31,9 +34,11 @@ export default class WebViewComponent extends ComponentBase implements IComponen
@Input() descriptor: IComponentDescriptor; @Input() descriptor: IComponentDescriptor;
@Input() modelStore: IModelStore; @Input() modelStore: IModelStore;
private static readonly standardSupportedLinkSchemes = ['http', 'https', 'mailto'];
private _webview: WebviewElement; private _webview: WebviewElement;
private _onMessage = new Emitter<any>();
private _renderedHtml: string; private _renderedHtml: string;
private _extensionLocationUri: URI;
protected contextKey: IContextKey<boolean>; protected contextKey: IContextKey<boolean>;
protected findInputFocusContextKey: IContextKey<boolean>; protected findInputFocusContextKey: IContextKey<boolean>;
@@ -46,6 +51,8 @@ export default class WebViewComponent extends ComponentBase implements IComponen
@Inject(IThemeService) private themeService: IThemeService, @Inject(IThemeService) private themeService: IThemeService,
@Inject(IEnvironmentService) private environmentService: IEnvironmentService, @Inject(IEnvironmentService) private environmentService: IEnvironmentService,
@Inject(IContextViewService) private contextViewService: IContextViewService, @Inject(IContextViewService) private contextViewService: IContextViewService,
@Inject(IOpenerService) private readonly _openerService: IOpenerService,
@Inject(IWorkspaceContextService) private readonly _contextService: IWorkspaceContextService,
@Inject(IInstantiationService) private instantiationService: IInstantiationService, @Inject(IInstantiationService) private instantiationService: IInstantiationService,
@Inject(IContextKeyService) contextKeyService: IContextKeyService @Inject(IContextKeyService) contextKeyService: IContextKeyService
) { ) {
@@ -72,6 +79,8 @@ export default class WebViewComponent extends ComponentBase implements IComponen
this._webview.mountTo(this._el.nativeElement); this._webview.mountTo(this._el.nativeElement);
this._register(this._webview.onDidClickLink(link => this.onDidClickLink(link)));
this._register(this._webview.onMessage(e => { this._register(this._webview.onMessage(e => {
this._onEventEmitter.fire({ this._onEventEmitter.fire({
eventType: ComponentEventType.onMessage, eventType: ComponentEventType.onMessage,
@@ -103,9 +112,28 @@ export default class WebViewComponent extends ComponentBase implements IComponen
} }
} }
private onDidClickLink(link: URI): any {
if (!link) {
return;
}
if (WebViewComponent.standardSupportedLinkSchemes.indexOf(link.scheme) >= 0 || this.enableCommandUris && link.scheme === 'command') {
this._openerService.open(link);
}
}
private get enableCommandUris(): boolean {
if (this.options && this.options.enableCommandUris) {
return true;
}
return false;
}
/// IComponent implementation /// IComponent implementation
public layout(): void { public layout(): void {
let element = <HTMLElement> this._el.nativeElement;
element.style.position = this.position;
this._webview.layout(); this._webview.layout();
} }
@@ -116,10 +144,17 @@ export default class WebViewComponent extends ComponentBase implements IComponen
public setProperties(properties: { [key: string]: any; }): void { public setProperties(properties: { [key: string]: any; }): void {
super.setProperties(properties); super.setProperties(properties);
if (this.options) {
this._webview.options = this.getExtendedOptions();
}
if (this.html !== this._renderedHtml) { if (this.html !== this._renderedHtml) {
this.setHtml(); this.setHtml();
} }
if (this.extensionLocation) {
this._extensionLocationUri = URI.revive(this.extensionLocation);
}
this.sendMessage(); this.sendMessage();
} }
// CSS-bound properties // CSS-bound properties
@@ -139,4 +174,38 @@ export default class WebViewComponent extends ComponentBase implements IComponen
public set html(newValue: string) { public set html(newValue: string) {
this.setPropertyFromUI<sqlops.WebViewProperties, string>((properties, html) => { properties.html = html; }, newValue); this.setPropertyFromUI<sqlops.WebViewProperties, string>((properties, html) => { properties.html = html; }, newValue);
} }
public get options(): vscode.WebviewOptions {
return this.getPropertyOrDefault<sqlops.WebViewProperties, vscode.WebviewOptions>((props) => props.options, undefined);
}
public get extensionLocation(): UriComponents {
return this.getPropertyOrDefault<sqlops.WebViewProperties, UriComponents>((props) => props.extensionLocation, undefined);
}
private get extensionLocationUri(): URI {
if (!this._extensionLocationUri && this.extensionLocation) {
this._extensionLocationUri = URI.revive(this.extensionLocation);
}
return this._extensionLocationUri;
}
private getExtendedOptions(): WebviewOptions {
let options = this.options || { enableScripts: true };
return {
allowScripts: options.enableScripts,
allowSvgs: true,
enableWrappedPostMessage: true,
useSameOriginForRoot: false,
localResourceRoots: options!.localResourceRoots || this.getDefaultLocalResourceRoots()
};
}
private getDefaultLocalResourceRoots(): URI[] {
const rootPaths = this._contextService.getWorkspace().folders.map(x => x.uri);
if (this.extensionLocationUri) {
rootPaths.push(this.extensionLocationUri);
}
return rootPaths;
}
} }

View File

@@ -287,7 +287,16 @@ declare module 'sqlops' {
/** /**
* *
*/ */
textAlign?: string textAlign?: string;
/**
* The position CSS property. Empty by default.
* This is particularly useful if laying out components inside a FlexContainer and
* the size of the component is meant to be a fixed size. In this case the position must be
* set to 'absolute', with the parent FlexContainer having 'relative' position.
* Without this the component will fail to correctly size itself.
*/
position?: string;
} }
export interface FlexItemLayout { export interface FlexItemLayout {
@@ -410,6 +419,14 @@ declare module 'sqlops' {
export interface ComponentProperties { export interface ComponentProperties {
height?: number | string; height?: number | string;
width?: number | string; width?: number | string;
/**
* The position CSS property. Empty by default.
* This is particularly useful if laying out components inside a FlexContainer and
* the size of the component is meant to be a fixed size. In this case the position must be
* set to 'absolute', with the parent FlexContainer having 'relative' position.
* Without this the component will fail to correctly size itself
*/
position?: string;
} }
export interface ComponentWithIcon { export interface ComponentWithIcon {
@@ -498,15 +515,25 @@ declare module 'sqlops' {
} }
export interface WebViewProperties { export interface WebViewProperties extends ComponentProperties {
message?: any; message?: any;
/**
* Contents of the webview.
*
* Should be a complete html document.
*/
html?: string; html?: string;
/**
* Content settings for the webview.
*/
options?: vscode.WebviewOptions;
} }
/** /**
* Editor properties for the editor component * Editor properties for the editor component
*/ */
export interface EditorProperties { export interface EditorProperties extends ComponentProperties {
/** /**
* The content inside the text editor * The content inside the text editor
*/ */
@@ -515,13 +542,6 @@ declare module 'sqlops' {
* The languge mode for this text editor. The language mode is SQL by default. * The languge mode for this text editor. The language mode is SQL by default.
*/ */
languageMode?: string; languageMode?: string;
/**
* The position CSS property for the editor. Empty by default.
* If the editor is included inside a FlexContainer this must be
* set to 'absolute', with the parent FlexContainer having 'relative' position.
* Without this the editor will fail to correctly size itself
*/
position?: string;
} }
export interface ButtonProperties extends ComponentProperties, ComponentWithIcon { export interface ButtonProperties extends ComponentProperties, ComponentWithIcon {
@@ -595,6 +615,7 @@ declare module 'sqlops' {
html: string; html: string;
message: any; message: any;
onMessage: vscode.Event<any>; onMessage: vscode.Event<any>;
readonly options: vscode.WebviewOptions;
} }
/** /**

View File

@@ -24,7 +24,9 @@ class ModelBuilderImpl implements sqlops.ModelBuilder {
private readonly _proxy: MainThreadModelViewShape, private readonly _proxy: MainThreadModelViewShape,
private readonly _handle: number, private readonly _handle: number,
private readonly _mainContext: IMainContext, private readonly _mainContext: IMainContext,
private readonly _extHostModelViewTree: ExtHostModelViewTreeViewsShape) { private readonly _extHostModelViewTree: ExtHostModelViewTreeViewsShape,
private readonly _extensionLocation: URI
) {
this.nextComponentId = 0; this.nextComponentId = 0;
} }
@@ -107,7 +109,7 @@ class ModelBuilderImpl implements sqlops.ModelBuilder {
webView(): sqlops.ComponentBuilder<sqlops.WebViewComponent> { webView(): sqlops.ComponentBuilder<sqlops.WebViewComponent> {
let id = this.getNextComponentId(); let id = this.getNextComponentId();
let builder: ComponentBuilderImpl<sqlops.WebViewComponent> = this.getComponentBuilder(new WebViewWrapper(this._proxy, this._handle, id), id); let builder: ComponentBuilderImpl<sqlops.WebViewComponent> = this.getComponentBuilder(new WebViewWrapper(this._proxy, this._handle, id, this._extensionLocation), id);
this._componentBuilders.set(id, builder); this._componentBuilders.set(id, builder);
return builder; return builder;
} }
@@ -224,7 +226,8 @@ class ComponentBuilderImpl<T extends sqlops.Component> implements sqlops.Compone
} }
withProperties<U>(properties: U): sqlops.ComponentBuilder<T> { withProperties<U>(properties: U): sqlops.ComponentBuilder<T> {
this._component.properties = properties; // Keep any properties that may have been set during initial object construction
this._component.properties = Object.assign({}, this._component.properties, properties);
return this; return this;
} }
@@ -804,10 +807,11 @@ class CheckBoxWrapper extends ComponentWrapper implements sqlops.CheckBoxCompone
} }
class WebViewWrapper extends ComponentWrapper implements sqlops.WebViewComponent { class WebViewWrapper extends ComponentWrapper implements sqlops.WebViewComponent {
constructor(proxy: MainThreadModelViewShape, handle: number, id: string, private _extensionLocation: URI) {
constructor(proxy: MainThreadModelViewShape, handle: number, id: string) {
super(proxy, handle, ModelComponentTypes.WebView, id); super(proxy, handle, ModelComponentTypes.WebView, id);
this.properties = {}; this.properties = {
'extensionLocation': this._extensionLocation
};
this._emitterMap.set(ComponentEventType.onMessage, new Emitter<any>()); this._emitterMap.set(ComponentEventType.onMessage, new Emitter<any>());
} }
@@ -829,6 +833,16 @@ class WebViewWrapper extends ComponentWrapper implements sqlops.WebViewComponent
let emitter = this._emitterMap.get(ComponentEventType.onMessage); let emitter = this._emitterMap.get(ComponentEventType.onMessage);
return emitter && emitter.event; return emitter && emitter.event;
} }
public get options(): vscode.WebviewOptions {
return this.properties['options'];
}
public set options(o: vscode.WebviewOptions) {
this.setProperty('options', o);
}
} }
class EditorWrapper extends ComponentWrapper implements sqlops.EditorComponent { class EditorWrapper extends ComponentWrapper implements sqlops.EditorComponent {
@@ -1168,9 +1182,10 @@ class ModelViewImpl implements sqlops.ModelView {
private readonly _connection: sqlops.connection.Connection, private readonly _connection: sqlops.connection.Connection,
private readonly _serverInfo: sqlops.ServerInfo, private readonly _serverInfo: sqlops.ServerInfo,
private readonly mainContext: IMainContext, private readonly mainContext: IMainContext,
private readonly _extHostModelViewTree: ExtHostModelViewTreeViewsShape private readonly _extHostModelViewTree: ExtHostModelViewTreeViewsShape,
private readonly _extensionLocation: URI
) { ) {
this._modelBuilder = new ModelBuilderImpl(this._proxy, this._handle, this.mainContext, this._extHostModelViewTree); this._modelBuilder = new ModelBuilderImpl(this._proxy, this._handle, this.mainContext, this._extHostModelViewTree, _extensionLocation);
} }
public get onClosed(): vscode.Event<any> { public get onClosed(): vscode.Event<any> {
@@ -1221,7 +1236,7 @@ export class ExtHostModelView implements ExtHostModelViewShape {
private readonly _modelViews = new Map<number, ModelViewImpl>(); private readonly _modelViews = new Map<number, ModelViewImpl>();
private readonly _handlers = new Map<string, (view: sqlops.ModelView) => void>(); private readonly _handlers = new Map<string, (view: sqlops.ModelView) => void>();
private readonly _handlerToExtensionPath = new Map<string, URI>();
constructor( constructor(
private _mainContext: IMainContext, private _mainContext: IMainContext,
private _extHostModelViewTree: ExtHostModelViewTreeViewsShape private _extHostModelViewTree: ExtHostModelViewTreeViewsShape
@@ -1235,13 +1250,15 @@ export class ExtHostModelView implements ExtHostModelViewShape {
this._modelViews.delete(handle); this._modelViews.delete(handle);
} }
$registerProvider(widgetId: string, handler: (webview: sqlops.ModelView) => void): void { $registerProvider(widgetId: string, handler: (webview: sqlops.ModelView) => void, extensionLocation: URI): void {
this._handlers.set(widgetId, handler); this._handlers.set(widgetId, handler);
this._handlerToExtensionPath.set(widgetId, extensionLocation);
this._proxy.$registerProvider(widgetId); this._proxy.$registerProvider(widgetId);
} }
$registerWidget(handle: number, id: string, connection: sqlops.connection.Connection, serverInfo: sqlops.ServerInfo): void { $registerWidget(handle: number, id: string, connection: sqlops.connection.Connection, serverInfo: sqlops.ServerInfo): void {
let view = new ModelViewImpl(this._proxy, handle, connection, serverInfo, this._mainContext, this._extHostModelViewTree); let extensionLocation = this._handlerToExtensionPath.get(id);
let view = new ModelViewImpl(this._proxy, handle, connection, serverInfo, this._mainContext, this._extHostModelViewTree, extensionLocation);
this._modelViews.set(handle, view); this._modelViews.set(handle, view);
this._handlers.get(id)(view); this._handlers.get(id)(view);
} }

View File

@@ -9,6 +9,7 @@ import { Event, Emitter } from 'vs/base/common/event';
import { deepClone } from 'vs/base/common/objects'; import { deepClone } from 'vs/base/common/objects';
import * as nls from 'vs/nls'; import * as nls from 'vs/nls';
import { generateUuid } from 'vs/base/common/uuid'; import { generateUuid } from 'vs/base/common/uuid';
import URI from 'vs/base/common/uri';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import * as sqlops from 'sqlops'; import * as sqlops from 'sqlops';
@@ -31,7 +32,8 @@ class ModelViewPanelImpl implements sqlops.window.modelviewdialog.ModelViewPanel
constructor(private _viewType: string, constructor(private _viewType: string,
protected _extHostModelViewDialog: ExtHostModelViewDialog, protected _extHostModelViewDialog: ExtHostModelViewDialog,
protected _extHostModelView: ExtHostModelViewShape) { protected _extHostModelView: ExtHostModelViewShape,
protected _extensionLocation: URI) {
this._onValidityChanged = this._extHostModelViewDialog.getValidityChangedEvent(this); this._onValidityChanged = this._extHostModelViewDialog.getValidityChangedEvent(this);
this._onValidityChanged(valid => this._valid = valid); this._onValidityChanged(valid => this._valid = valid);
} }
@@ -43,7 +45,7 @@ class ModelViewPanelImpl implements sqlops.window.modelviewdialog.ModelViewPanel
this._extHostModelView.$registerProvider(viewId, modelView => { this._extHostModelView.$registerProvider(viewId, modelView => {
this._modelView = modelView; this._modelView = modelView;
handler(modelView); handler(modelView);
}); }, this._extensionLocation);
} }
} }
@@ -76,11 +78,12 @@ class ModelViewEditorImpl extends ModelViewPanelImpl implements sqlops.workspace
constructor( constructor(
extHostModelViewDialog: ExtHostModelViewDialog, extHostModelViewDialog: ExtHostModelViewDialog,
extHostModelView: ExtHostModelViewShape, extHostModelView: ExtHostModelViewShape,
extensionLocation: URI,
private _proxy: MainThreadModelViewDialogShape, private _proxy: MainThreadModelViewDialogShape,
private _title: string, private _title: string,
private _options: sqlops.ModelViewEditorOptions private _options: sqlops.ModelViewEditorOptions
) { ) {
super('modelViewEditor', extHostModelViewDialog, extHostModelView); super('modelViewEditor', extHostModelViewDialog, extHostModelView, extensionLocation);
} }
public openEditor(position?: vscode.ViewColumn): Thenable<void> { public openEditor(position?: vscode.ViewColumn): Thenable<void> {
@@ -100,8 +103,9 @@ class DialogImpl extends ModelViewPanelImpl implements sqlops.window.modelviewdi
constructor(extHostModelViewDialog: ExtHostModelViewDialog, constructor(extHostModelViewDialog: ExtHostModelViewDialog,
extHostModelView: ExtHostModelViewShape, extHostModelView: ExtHostModelViewShape,
extHostTaskManagement: ExtHostBackgroundTaskManagementShape) { extHostTaskManagement: ExtHostBackgroundTaskManagementShape,
super('modelViewDialog', extHostModelViewDialog, extHostModelView); extensionLocation: URI) {
super('modelViewDialog', extHostModelViewDialog, extHostModelView, extensionLocation);
this.okButton = this._extHostModelViewDialog.createButton(DONE_LABEL); this.okButton = this._extHostModelViewDialog.createButton(DONE_LABEL);
this.cancelButton = this._extHostModelViewDialog.createButton(CANCEL_LABEL); this.cancelButton = this._extHostModelViewDialog.createButton(CANCEL_LABEL);
this._operationHandler = new BackgroundOperationHandler('dialog', extHostTaskManagement); this._operationHandler = new BackgroundOperationHandler('dialog', extHostTaskManagement);
@@ -144,8 +148,9 @@ class DialogImpl extends ModelViewPanelImpl implements sqlops.window.modelviewdi
class TabImpl extends ModelViewPanelImpl implements sqlops.window.modelviewdialog.DialogTab { class TabImpl extends ModelViewPanelImpl implements sqlops.window.modelviewdialog.DialogTab {
constructor( constructor(
extHostModelViewDialog: ExtHostModelViewDialog, extHostModelViewDialog: ExtHostModelViewDialog,
extHostModelView: ExtHostModelViewShape) { extHostModelView: ExtHostModelViewShape,
super('modelViewDialogTab', extHostModelViewDialog, extHostModelView); extensionLocation: URI) {
super('modelViewDialogTab', extHostModelViewDialog, extHostModelView, extensionLocation);
} }
public title: string; public title: string;
@@ -239,8 +244,9 @@ class WizardPageImpl extends ModelViewPanelImpl implements sqlops.window.modelvi
constructor(public title: string, constructor(public title: string,
extHostModelViewDialog: ExtHostModelViewDialog, extHostModelViewDialog: ExtHostModelViewDialog,
extHostModelView: ExtHostModelViewShape) { extHostModelView: ExtHostModelViewShape,
super('modelViewWizardPage', extHostModelViewDialog, extHostModelView); extensionLocation: URI) {
super('modelViewWizardPage', extHostModelViewDialog, extHostModelView, extensionLocation);
} }
public get enabled(): boolean { public get enabled(): boolean {
@@ -475,8 +481,8 @@ export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape {
this._proxy.$closeDialog(handle); this._proxy.$closeDialog(handle);
} }
public createModelViewEditor(title: string, options?: sqlops.ModelViewEditorOptions): sqlops.workspace.ModelViewEditor { public createModelViewEditor(title: string, extensionLocation: URI, options?: sqlops.ModelViewEditorOptions): sqlops.workspace.ModelViewEditor {
let editor = new ModelViewEditorImpl(this, this._extHostModelView, this._proxy, title, options); let editor = new ModelViewEditorImpl(this, this._extHostModelView, extensionLocation, this._proxy, title, options);
editor.handle = this.getHandle(editor); editor.handle = this.getHandle(editor);
return editor; return editor;
} }
@@ -524,15 +530,15 @@ export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape {
this._onClickCallbacks.set(handle, callback); this._onClickCallbacks.set(handle, callback);
} }
public createDialog(title: string): sqlops.window.modelviewdialog.Dialog { public createDialog(title: string, extensionLocation?: URI): sqlops.window.modelviewdialog.Dialog {
let dialog = new DialogImpl(this, this._extHostModelView, this._extHostTaskManagement); let dialog = new DialogImpl(this, this._extHostModelView, this._extHostTaskManagement, extensionLocation);
dialog.title = title; dialog.title = title;
dialog.handle = this.getHandle(dialog); dialog.handle = this.getHandle(dialog);
return dialog; return dialog;
} }
public createTab(title: string): sqlops.window.modelviewdialog.DialogTab { public createTab(title: string, extensionLocation?: URI): sqlops.window.modelviewdialog.DialogTab {
let tab = new TabImpl(this, this._extHostModelView); let tab = new TabImpl(this, this._extHostModelView, extensionLocation);
tab.title = title; tab.title = title;
tab.handle = this.getHandle(tab); tab.handle = this.getHandle(tab);
return tab; return tab;
@@ -561,8 +567,8 @@ export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape {
this._pageInfoChangedCallbacks.set(handle, callback); this._pageInfoChangedCallbacks.set(handle, callback);
} }
public createWizardPage(title: string): sqlops.window.modelviewdialog.WizardPage { public createWizardPage(title: string, extensionLocation?: URI): sqlops.window.modelviewdialog.WizardPage {
let page = new WizardPageImpl(title, this, this._extHostModelView); let page = new WizardPageImpl(title, this, this._extHostModelView, extensionLocation);
page.handle = this.getHandle(page); page.handle = this.getHandle(page);
return page; return page;
} }

View File

@@ -326,10 +326,10 @@ export function createApiFactory(
const modelViewDialog: typeof sqlops.window.modelviewdialog = { const modelViewDialog: typeof sqlops.window.modelviewdialog = {
createDialog(title: string): sqlops.window.modelviewdialog.Dialog { createDialog(title: string): sqlops.window.modelviewdialog.Dialog {
return extHostModelViewDialog.createDialog(title); return extHostModelViewDialog.createDialog(title, extension.extensionLocation);
}, },
createTab(title: string): sqlops.window.modelviewdialog.DialogTab { createTab(title: string): sqlops.window.modelviewdialog.DialogTab {
return extHostModelViewDialog.createTab(title); return extHostModelViewDialog.createTab(title, extension.extensionLocation);
}, },
createButton(label: string): sqlops.window.modelviewdialog.Button { createButton(label: string): sqlops.window.modelviewdialog.Button {
return extHostModelViewDialog.createButton(label); return extHostModelViewDialog.createButton(label);
@@ -370,7 +370,7 @@ export function createApiFactory(
onDidOpenDashboard: extHostDashboard.onDidOpenDashboard, onDidOpenDashboard: extHostDashboard.onDidOpenDashboard,
onDidChangeToDashboard: extHostDashboard.onDidChangeToDashboard, onDidChangeToDashboard: extHostDashboard.onDidChangeToDashboard,
createModelViewEditor(title: string, options?: sqlops.ModelViewEditorOptions): sqlops.workspace.ModelViewEditor { createModelViewEditor(title: string, options?: sqlops.ModelViewEditorOptions): sqlops.workspace.ModelViewEditor {
return extHostModelViewDialog.createModelViewEditor(title, options); return extHostModelViewDialog.createModelViewEditor(title, extension.extensionLocation, options);
} }
}; };
@@ -382,7 +382,7 @@ export function createApiFactory(
const ui = { const ui = {
registerModelViewProvider(modelViewId: string, handler: (view: sqlops.ModelView) => void): void { registerModelViewProvider(modelViewId: string, handler: (view: sqlops.ModelView) => void): void {
extHostModelView.$registerProvider(modelViewId, handler); extHostModelView.$registerProvider(modelViewId, handler, extension.extensionLocation);
} }
}; };

View File

@@ -9,6 +9,7 @@ import {
createExtHostContextProxyIdentifier as createExtId, createExtHostContextProxyIdentifier as createExtId,
ProxyIdentifier, IRPCProtocol ProxyIdentifier, IRPCProtocol
} from 'vs/workbench/services/extensions/node/proxyIdentifier'; } from 'vs/workbench/services/extensions/node/proxyIdentifier';
import URI, { UriComponents } from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base'; import { TPromise } from 'vs/base/common/winjs.base';
import { IDisposable } from 'vs/base/common/lifecycle'; import { IDisposable } from 'vs/base/common/lifecycle';
@@ -606,7 +607,7 @@ export interface MainThreadDashboardWebviewShape extends IDisposable {
} }
export interface ExtHostModelViewShape { export interface ExtHostModelViewShape {
$registerProvider(widgetId: string, handler: (webview: sqlops.ModelView) => void): void; $registerProvider(widgetId: string, handler: (webview: sqlops.ModelView) => void, extensionLocation: UriComponents): void;
$onClosed(handle: number): void; $onClosed(handle: number): void;
$registerWidget(handle: number, id: string, connection: sqlops.connection.Connection, serverInfo: sqlops.ServerInfo): void; $registerWidget(handle: number, id: string, connection: sqlops.connection.Connection, serverInfo: sqlops.ServerInfo): void;
$handleEvent(handle: number, id: string, eventArgs: any); $handleEvent(handle: number, id: string, eventArgs: any);

View File

@@ -69,7 +69,7 @@ suite('ExtHostModelView Validation Tests', () => {
.component(); .component();
await view.initializeModel(flexContainer); await view.initializeModel(flexContainer);
done(); done();
}); }, undefined);
extHostModelView.$registerWidget(handle, widgetId, undefined, undefined); extHostModelView.$registerWidget(handle, widgetId, undefined, undefined);
}); });

View File

@@ -35,7 +35,7 @@ suite('ExtHostModelViewDialog Tests', () => {
}; };
extHostModelView = Mock.ofInstance(<ExtHostModelViewShape>{ extHostModelView = Mock.ofInstance(<ExtHostModelViewShape>{
$registerProvider: (widget, handler) => undefined $registerProvider: (widget, handler, extensionLocation) => undefined
}); });
extHostModelViewDialog = new ExtHostModelViewDialog(mainContext, extHostModelView.object, undefined); extHostModelViewDialog = new ExtHostModelViewDialog(mainContext, extHostModelView.object, undefined);
}); });