Added empty kernel and hook up with Kernel drop down (#3173)

* Initial toolbar work
- This is in-progress and needs additional fixes

* Resolve PR comments

* Added empty kernel and hook up with Kernel dropdown

* Resolve PR comments
This commit is contained in:
Yurong He
2018-11-08 14:27:20 -08:00
committed by GitHub
parent bbf6cbd8fb
commit 7508192ab9
12 changed files with 336 additions and 33 deletions

View File

@@ -5,8 +5,7 @@
*--------------------------------------------------------------------------------------------*/
-->
<div style="overflow: hidden; width: 100%; height: 100%; display: flex; flex-flow: row">
<div #toolbar class="toolbar" style="flex: 0 0 auto;">
Toolbar
<div #toolbar class="toolbar actionbar-container" style="flex: 0 0 auto; display: flex; flex-flow: row; width: 40px; height: 40px; align-items: center; orientation: portrait">
</div>
<div #editor class="editor" style="flex: 1 1 auto; overflow: hidden;">
</div>

View File

@@ -23,7 +23,10 @@ import { Schemas } from 'vs/base/common/network';
import * as DOM from 'vs/base/browser/dom';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { ICellModel } from 'sql/parts/notebook/models/modelInterfaces';
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
import { RunCellAction } from 'sql/parts/notebook/cellViews/codeActions';
export const CODE_SELECTOR: string = 'code-component';
@@ -37,6 +40,7 @@ export class CodeComponent extends AngularDisposable implements OnInit {
@Input() cellModel: ICellModel;
@Output() public onContentChanged = new EventEmitter<void>();
protected _actionBar: Taskbar;
private readonly _minimumHeight = 30;
private _editor: QueryTextEditor;
private _editorInput: UntitledEditorInput;
@@ -49,7 +53,9 @@ export class CodeComponent extends AngularDisposable implements OnInit {
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
@Inject(IInstantiationService) private _instantiationService: IInstantiationService,
@Inject(IModelService) private _modelService: IModelService,
@Inject(IModeService) private _modeService: IModeService
@Inject(IModeService) private _modeService: IModeService,
@Inject(IContextMenuService) private contextMenuService: IContextMenuService,
@Inject(IContextViewService) private contextViewService: IContextViewService
) {
super();
}
@@ -57,6 +63,7 @@ export class CodeComponent extends AngularDisposable implements OnInit {
ngOnInit() {
this._register(this.themeService.onDidColorThemeChange(this.updateTheme, this));
this.updateTheme(this.themeService.getColorTheme());
this.initActionBar();
}
ngOnChanges() {
@@ -102,6 +109,18 @@ export class CodeComponent extends AngularDisposable implements OnInit {
this._editor.setHeightToScrollHeight();
}
protected initActionBar() {
let runCellAction = this._instantiationService.createInstance(RunCellAction);
let taskbar = <HTMLElement>this.toolbarElement.nativeElement;
this._actionBar = new Taskbar(taskbar, this.contextMenuService);
this._actionBar.context = this;
this._actionBar.setContent([
{ action: runCellAction }
]);
}
private createUri(): URI {
let uri = URI.from({ scheme: Schemas.untitled, path: `notebook-editor-${this.cellModel.id}` });
// Use this to set the internal (immutable) and public (shared with extension) uri properties

View File

@@ -0,0 +1,30 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Action } from 'vs/base/common/actions';
import { TPromise } from 'vs/base/common/winjs.base';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
import { ISelectBoxOptions } from 'vs/base/browser/ui/selectBox/selectBox';
import { TaskHistoryViewlet } from 'sql/parts/taskHistory/viewlet/taskHistoryViewlet';
export class RunCellAction extends Action {
public static ID = 'jobaction.notebookRunCell';
public static LABEL = 'Run cell';
constructor(
) {
super(RunCellAction.ID, RunCellAction.LABEL, 'newStepIcon');
}
public run(context: any): TPromise<boolean> {
return new TPromise<boolean>((resolve, reject) => {
try {
resolve(true);
} catch (e) {
reject(e);
}
});
}
}

View File

@@ -119,14 +119,14 @@ export class ClientSession implements IClientSession {
path: this.notebookUri.fsPath,
kernelName: undefined
});
session.defaultKernelLoaded = false;
} else {
throw err;
}
session.defaultKernelLoaded = false;
}
this._session = session;
await this.runKernelConfigActions(kernelName);
this._statusChangedEmitter.fire(session);
this._session = session;
await this.runKernelConfigActions(kernelName);
this._statusChangedEmitter.fire(session);
}
private async runKernelConfigActions(kernelName: string): Promise<void> {

View File

@@ -36,7 +36,7 @@ export class NotebookConnection {
}
public get connectionProfile(): IConnectionProfile {
return this.connectionProfile;
return this._connectionProfile;
}
@@ -70,11 +70,11 @@ export class NotebookConnection {
}
public get user(): string {
return this.connectionProfile.options[constants.userPropName];
return this._connectionProfile.options[constants.userPropName];
}
public get password(): string {
return this.connectionProfile.options[constants.passwordPropName];
return this._connectionProfile.options[constants.passwordPropName];
}
public get knoxport(): string {

View File

@@ -148,7 +148,7 @@ export class SparkMagicContexts {
} else {
// Handle kernels
if (savedKernelInfo && savedKernelInfo.name.toLowerCase().indexOf('spark') > -1) {
notificationService.warn(localize('sparkKernelRequiresConnection', 'Cannot use kernel {0} as no connection is active. The default kernel of Python3 will be used instead.', savedKernelInfo.display_name));
notificationService.warn(localize('sparkKernelRequiresConnection', 'Cannot use kernel {0} as no connection is active. The default kernel of {1} will be used instead.', savedKernelInfo.display_name, defaultKernel.display_name));
}
}

View File

@@ -5,10 +5,7 @@
*--------------------------------------------------------------------------------------------*/
-->
<div style="overflow: hidden; width: 100%; height: 100%; display: flex; flex-flow: column">
<div #toolbar class="editor-toolbar" style="flex: 0 0 auto; display: flex; flex-flow: row; width: 100%; align-items: center">
<div style="flex: 1 1 auto">
PlaceHolder for Toolbar
</div>
<div #toolbar class="editor-toolbar actionbar-container" style="flex: 0 0 auto; display: flex; flex-flow: row; width: 100%; align-items: center; height: 40px">
</div>
<div class="scrollable" style="flex: 1 1 auto; position: relative">
<loading-spinner [loading]="isLoading"></loading-spinner>

View File

@@ -28,6 +28,11 @@ import { ModelFactory } from 'sql/parts/notebook/models/modelFactory';
import * as notebookUtils from './notebookUtils';
import { Deferred } from 'sql/base/common/promise';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { AddCellAction, KernelsDropdown, AttachToDropdown } from 'sql/parts/notebook/notebookActions';
import { attachSelectBoxStyler } from 'vs/platform/theme/common/styler';
export const NOTEBOOK_SELECTOR: string = 'notebook-component';
@@ -41,10 +46,12 @@ export class NotebookComponent extends AngularDisposable implements OnInit {
private _model: NotebookModel;
private _isInErrorState: boolean = false;
private _errorMessage: string;
protected _actionBar: Taskbar;
private _activeCell: ICellModel;
protected isLoading: boolean;
private notebookManager: INotebookManager;
private _modelReadyDeferred = new Deferred<NotebookModel>();
private _modelRegisteredDeferred = new Deferred<NotebookModel>();
private profile: IConnectionProfile;
@@ -55,7 +62,10 @@ export class NotebookComponent extends AngularDisposable implements OnInit {
@Inject(IConnectionManagementService) private connectionManagementService: IConnectionManagementService,
@Inject(INotificationService) private notificationService: INotificationService,
@Inject(INotebookService) private notebookService: INotebookService,
@Inject(IBootstrapParams) private notebookParams: INotebookParams
@Inject(IBootstrapParams) private notebookParams: INotebookParams,
@Inject(IInstantiationService) private instantiationService: IInstantiationService,
@Inject(IContextMenuService) private contextMenuService: IContextMenuService,
@Inject(IContextViewService) private contextViewService: IContextViewService
) {
super();
this.profile = this.notebookParams!.profile;
@@ -65,9 +75,14 @@ export class NotebookComponent extends AngularDisposable implements OnInit {
ngOnInit() {
this._register(this.themeService.onDidColorThemeChange(this.updateTheme, this));
this.updateTheme(this.themeService.getColorTheme());
this.initActionBar();
this.doLoad();
}
public get modelRegistered(): Promise<NotebookModel> {
return this._modelRegisteredDeferred.promise;
}
protected get cells(): ReadonlyArray<ICellModel> {
return this._model ? this._model.cells : [];
}
@@ -135,11 +150,12 @@ export class NotebookComponent extends AngularDisposable implements OnInit {
notebookManager: this.notebookManager
}, false, this.profile);
model.onError((errInfo: INotification) => this.handleModelError(errInfo));
model.backgroundStartSession();
await model.requestModelLoad(this.notebookParams.isTrusted);
model.contentChanged((change) => this.handleContentChanged(change));
this._model = model;
this._register(model);
this._modelRegisteredDeferred.resolve(this._model);
model.backgroundStartSession();
this._changeRef.detectChanges();
}
@@ -170,6 +186,38 @@ export class NotebookComponent extends AngularDisposable implements OnInit {
this.notificationService.error(error);
}
protected initActionBar() {
let kernelInfoText = document.createElement('div');
kernelInfoText.className ='notebook-info-label';
kernelInfoText.innerText = 'Kernel: ';
let kernelsDropdown = new KernelsDropdown(this.contextViewService, this.modelRegistered);
let kernelsDropdownTemplateContainer = document.createElement('div');
kernelsDropdownTemplateContainer.className = 'notebook-toolbar-dropdown';
kernelsDropdown.render(kernelsDropdownTemplateContainer);
attachSelectBoxStyler(kernelsDropdown, this.themeService);
let attachToDropdown = new AttachToDropdown(this.contextViewService);
let attachToDropdownTemplateContainer = document.createElement('div');
attachToDropdownTemplateContainer.className = 'notebook-toolbar-dropdown';
attachToDropdown.render(attachToDropdownTemplateContainer);
attachSelectBoxStyler(attachToDropdown, this.themeService);
let attachToInfoText = document.createElement('div');
attachToInfoText.className ='notebook-info-label';
attachToInfoText.innerText = 'Attach To: ';
let taskbar = <HTMLElement>this.toolbar.nativeElement;
this._actionBar = new Taskbar(taskbar, this.contextMenuService);
this._actionBar.context = this;
this._actionBar.setContent([
{ element: kernelInfoText },
{ element: kernelsDropdownTemplateContainer },
{ element: attachToInfoText },
{ element: attachToDropdownTemplateContainer }
]);
}
public async save(): Promise<boolean> {
try {
let saved = await this._model.saveModel();

View File

@@ -10,13 +10,12 @@ import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { Action } from 'vs/base/common/actions';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { TPromise } from 'vs/base/common/winjs.base';
import * as nls from 'vs/nls';
import { Schemas } from 'vs/base/common/network';
import URI from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import { NotebookInput, NotebookInputModel } from 'sql/parts/notebook/notebookInput';
import { NotebookEditor } from 'sql/parts/notebook/notebookEditor';
import URI from 'vs/base/common/uri';
let counter = 0;
@@ -27,7 +26,7 @@ let counter = 0;
export class OpenNotebookAction extends Action {
public static ID = 'OpenNotebookAction';
public static LABEL = nls.localize('OpenNotebookAction', 'Open Notebook editor');
public static LABEL = localize('OpenNotebookAction', 'Open Notebook editor');
constructor(
id: string,
@@ -67,4 +66,6 @@ actionRegistry.registerWorkbenchAction(
OpenNotebookAction.LABEL
),
OpenNotebookAction.LABEL
);
);

View File

@@ -12,3 +12,15 @@
border-width: 1px;
border-style: solid;
}
.notebookEditor .notebook-toolbar-dropdown {
width: 150px;
padding-right: 10px;
}
.notebookEditor .notebook-info-label {
padding-right: 5px;
text-align: center;
display: flex;
align-items: center;
}

View File

@@ -0,0 +1,87 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as sqlops from 'sqlops';
import { Action } from 'vs/base/common/actions';
import { TPromise } from 'vs/base/common/winjs.base';
import { localize } from 'vs/nls';
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
import { INotebookModel } from 'sql/parts/notebook/models/modelInterfaces';
const msgLoading = localize('loading', 'Loading kernels...');
export class AddCellAction extends Action {
public static ID = 'notebook.addCell';
public static LABEL = 'Cell';
constructor(
) {
super(AddCellAction.ID, AddCellAction.LABEL, 'newStepIcon');
}
public run(context: any): TPromise<boolean> {
return new TPromise<boolean>((resolve, reject) => {
try {
resolve(true);
} catch (e) {
reject(e);
}
});
}
}
export class KernelsDropdown extends SelectBox {
private model: INotebookModel;
constructor(contextViewProvider: IContextViewProvider, modelRegistered: Promise<INotebookModel>
) {
super( [msgLoading], msgLoading, contextViewProvider);
if (modelRegistered) {
modelRegistered
.then((model) => this.updateModel(model))
.catch((err) => {
// No-op for now
});
}
this.onDidSelect(e => this.doChangeKernel(e.selected));
}
updateModel(model: INotebookModel): void {
this.model = model;
model.kernelsChanged((defaultKernel) => {
this.updateKernel(defaultKernel);
});
if (model.clientSession) {
model.clientSession.kernelChanged((changedArgs: sqlops.nb.IKernelChangedArgs) => {
if (changedArgs.newValue) {
this.updateKernel(changedArgs.newValue);
}
});
}
}
// Update SelectBox values
private updateKernel(defaultKernel: sqlops.nb.IKernelSpec) {
let specs = this.model.specs;
if (specs && specs.kernels) {
let index = specs.kernels.findIndex((kernel => kernel.name === defaultKernel.name));
this.setOptions(specs.kernels.map(kernel => kernel.display_name), index);
}
}
public doChangeKernel(displayName: string): void {
this.model.changeKernel(displayName);
}
}
export class AttachToDropdown extends SelectBox {
constructor(contextViewProvider: IContextViewProvider
) {
let options: string[] = ['localhost'];
super(options, 'localhost', contextViewProvider);
}
}

View File

@@ -1,31 +1,141 @@
'use strict';
import { nb } from 'sqlops';
import { Session } from 'electron';
import { localize } from 'vs/nls';
const noKernel: string = localize('noKernel', 'No Kernel');
let noKernelSpec: nb.IKernelSpec = ({
name: noKernel,
language: 'python',
display_name: noKernel
});
export class SessionManager implements nb.SessionManager {
private _sessionManager: nb.SessionManager;
constructor() {
}
public get isReady(): boolean {
return this._sessionManager.isReady;
return true;
}
public get ready(): Thenable<void> {
return this._sessionManager.ready;
return Promise.resolve();
}
public get specs(): nb.IAllKernels {
return this._sessionManager.specs;
let allKernels: nb.IAllKernels = {
defaultKernel: noKernel,
kernels: [noKernelSpec]
};
return allKernels;
}
startNew(options: nb.ISessionOptions): Thenable<nb.ISession> {
return this._sessionManager.startNew(options);
let session = new EmptySession(options);
return Promise.resolve(session);
}
shutdown(id: string): Thenable<void> {
return this.shutdown(id);
return Promise.resolve();
}
}
class EmptySession implements nb.ISession {
private _kernel: EmptyKernel;
private _defaultKernelLoaded = false;
public set defaultKernelLoaded(value) {
this._defaultKernelLoaded = value;
}
public get defaultKernelLoaded(): boolean {
return this._defaultKernelLoaded;
}
constructor(private options: nb.ISessionOptions) {
this._kernel = new EmptyKernel();
}
public get canChangeKernels(): boolean {
return true;
}
public get id(): string {
return this.options.kernelId || '';
}
public get path(): string {
return this.options.path;
}
public get name(): string {
return this.options.name || '';
}
public get type(): string {
return this.options.type || '';
}
public get status(): nb.KernelStatus {
return 'connected';
}
public get kernel(): nb.IKernel {
return this._kernel;
}
changeKernel(kernelInfo: nb.IKernelSpec): Thenable<nb.IKernel> {
return Promise.resolve(this.kernel);
}
}
class EmptyKernel implements nb.IKernel {
public get id(): string {
return '-1';
}
public get name(): string {
return noKernel;
}
public get supportsIntellisense(): boolean {
return false;
}
public get isReady(): boolean {
return true;
}
public get ready(): Thenable<void> {
return Promise.resolve();
}
public get info(): nb.IInfoReply {
let info: nb.IInfoReply = {
protocol_version: '',
implementation: '',
implementation_version: '',
language_info: {
name: '',
version: '',
},
banner: '',
help_links: [{
text: '',
url: ''
}]
};
return info;
}
getSpec(): Thenable<nb.IKernelSpec> {
return Promise.resolve(noKernelSpec);
}
requestExecute(content: nb.IExecuteRequest, disposeOnDone?: boolean): nb.IFuture {
throw new Error('Method not implemented.');
}
requestComplete(content: nb.ICompleteRequest): Thenable<nb.ICompleteReplyMsg> {
let response: Partial<nb.ICompleteReplyMsg> = { };
return Promise.resolve(response as nb.ICompleteReplyMsg);
}
}