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 style="overflow: hidden; width: 100%; height: 100%; display: flex; flex-flow: row">
<div #toolbar class="toolbar" style="flex: 0 0 auto;"> <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">
Toolbar
</div> </div>
<div #editor class="editor" style="flex: 1 1 auto; overflow: hidden;"> <div #editor class="editor" style="flex: 1 1 auto; overflow: hidden;">
</div> </div>

View File

@@ -23,7 +23,10 @@ import { Schemas } from 'vs/base/common/network';
import * as DOM from 'vs/base/browser/dom'; import * as DOM from 'vs/base/browser/dom';
import { IModeService } from 'vs/editor/common/services/modeService'; import { IModeService } from 'vs/editor/common/services/modeService';
import { IModelService } from 'vs/editor/common/services/modelService'; 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 { 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'; export const CODE_SELECTOR: string = 'code-component';
@@ -37,6 +40,7 @@ export class CodeComponent extends AngularDisposable implements OnInit {
@Input() cellModel: ICellModel; @Input() cellModel: ICellModel;
@Output() public onContentChanged = new EventEmitter<void>(); @Output() public onContentChanged = new EventEmitter<void>();
protected _actionBar: Taskbar;
private readonly _minimumHeight = 30; private readonly _minimumHeight = 30;
private _editor: QueryTextEditor; private _editor: QueryTextEditor;
private _editorInput: UntitledEditorInput; private _editorInput: UntitledEditorInput;
@@ -49,7 +53,9 @@ export class CodeComponent extends AngularDisposable implements OnInit {
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService, @Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
@Inject(IInstantiationService) private _instantiationService: IInstantiationService, @Inject(IInstantiationService) private _instantiationService: IInstantiationService,
@Inject(IModelService) private _modelService: IModelService, @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(); super();
} }
@@ -57,6 +63,7 @@ export class CodeComponent extends AngularDisposable implements OnInit {
ngOnInit() { ngOnInit() {
this._register(this.themeService.onDidColorThemeChange(this.updateTheme, this)); this._register(this.themeService.onDidColorThemeChange(this.updateTheme, this));
this.updateTheme(this.themeService.getColorTheme()); this.updateTheme(this.themeService.getColorTheme());
this.initActionBar();
} }
ngOnChanges() { ngOnChanges() {
@@ -102,6 +109,18 @@ export class CodeComponent extends AngularDisposable implements OnInit {
this._editor.setHeightToScrollHeight(); 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 { private createUri(): URI {
let uri = URI.from({ scheme: Schemas.untitled, path: `notebook-editor-${this.cellModel.id}` }); 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 // 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, path: this.notebookUri.fsPath,
kernelName: undefined kernelName: undefined
}); });
session.defaultKernelLoaded = false;
} else { } else {
throw err; throw err;
} }
session.defaultKernelLoaded = false;
} }
this._session = session; this._session = session;
await this.runKernelConfigActions(kernelName); await this.runKernelConfigActions(kernelName);
this._statusChangedEmitter.fire(session); this._statusChangedEmitter.fire(session);
} }
private async runKernelConfigActions(kernelName: string): Promise<void> { private async runKernelConfigActions(kernelName: string): Promise<void> {

View File

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

View File

@@ -148,7 +148,7 @@ export class SparkMagicContexts {
} else { } else {
// Handle kernels // Handle kernels
if (savedKernelInfo && savedKernelInfo.name.toLowerCase().indexOf('spark') > -1) { 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 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 #toolbar class="editor-toolbar actionbar-container" style="flex: 0 0 auto; display: flex; flex-flow: row; width: 100%; align-items: center; height: 40px">
<div style="flex: 1 1 auto">
PlaceHolder for Toolbar
</div>
</div> </div>
<div class="scrollable" style="flex: 1 1 auto; position: relative"> <div class="scrollable" style="flex: 1 1 auto; position: relative">
<loading-spinner [loading]="isLoading"></loading-spinner> <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 * as notebookUtils from './notebookUtils';
import { Deferred } from 'sql/base/common/promise'; import { Deferred } from 'sql/base/common/promise';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; 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'; export const NOTEBOOK_SELECTOR: string = 'notebook-component';
@@ -41,10 +46,12 @@ export class NotebookComponent extends AngularDisposable implements OnInit {
private _model: NotebookModel; private _model: NotebookModel;
private _isInErrorState: boolean = false; private _isInErrorState: boolean = false;
private _errorMessage: string; private _errorMessage: string;
protected _actionBar: Taskbar;
private _activeCell: ICellModel; private _activeCell: ICellModel;
protected isLoading: boolean; protected isLoading: boolean;
private notebookManager: INotebookManager; private notebookManager: INotebookManager;
private _modelReadyDeferred = new Deferred<NotebookModel>(); private _modelReadyDeferred = new Deferred<NotebookModel>();
private _modelRegisteredDeferred = new Deferred<NotebookModel>();
private profile: IConnectionProfile; private profile: IConnectionProfile;
@@ -55,7 +62,10 @@ export class NotebookComponent extends AngularDisposable implements OnInit {
@Inject(IConnectionManagementService) private connectionManagementService: IConnectionManagementService, @Inject(IConnectionManagementService) private connectionManagementService: IConnectionManagementService,
@Inject(INotificationService) private notificationService: INotificationService, @Inject(INotificationService) private notificationService: INotificationService,
@Inject(INotebookService) private notebookService: INotebookService, @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(); super();
this.profile = this.notebookParams!.profile; this.profile = this.notebookParams!.profile;
@@ -65,9 +75,14 @@ export class NotebookComponent extends AngularDisposable implements OnInit {
ngOnInit() { ngOnInit() {
this._register(this.themeService.onDidColorThemeChange(this.updateTheme, this)); this._register(this.themeService.onDidColorThemeChange(this.updateTheme, this));
this.updateTheme(this.themeService.getColorTheme()); this.updateTheme(this.themeService.getColorTheme());
this.initActionBar();
this.doLoad(); this.doLoad();
} }
public get modelRegistered(): Promise<NotebookModel> {
return this._modelRegisteredDeferred.promise;
}
protected get cells(): ReadonlyArray<ICellModel> { protected get cells(): ReadonlyArray<ICellModel> {
return this._model ? this._model.cells : []; return this._model ? this._model.cells : [];
} }
@@ -135,11 +150,12 @@ export class NotebookComponent extends AngularDisposable implements OnInit {
notebookManager: this.notebookManager notebookManager: this.notebookManager
}, false, this.profile); }, false, this.profile);
model.onError((errInfo: INotification) => this.handleModelError(errInfo)); model.onError((errInfo: INotification) => this.handleModelError(errInfo));
model.backgroundStartSession();
await model.requestModelLoad(this.notebookParams.isTrusted); await model.requestModelLoad(this.notebookParams.isTrusted);
model.contentChanged((change) => this.handleContentChanged(change)); model.contentChanged((change) => this.handleContentChanged(change));
this._model = model; this._model = model;
this._register(model); this._register(model);
this._modelRegisteredDeferred.resolve(this._model);
model.backgroundStartSession();
this._changeRef.detectChanges(); this._changeRef.detectChanges();
} }
@@ -170,6 +186,38 @@ export class NotebookComponent extends AngularDisposable implements OnInit {
this.notificationService.error(error); 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> { public async save(): Promise<boolean> {
try { try {
let saved = await this._model.saveModel(); 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 { Action } from 'vs/base/common/actions';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { TPromise } from 'vs/base/common/winjs.base'; import { TPromise } from 'vs/base/common/winjs.base';
import * as nls from 'vs/nls';
import { Schemas } from 'vs/base/common/network'; 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 { NotebookInput, NotebookInputModel } from 'sql/parts/notebook/notebookInput';
import { NotebookEditor } from 'sql/parts/notebook/notebookEditor'; import { NotebookEditor } from 'sql/parts/notebook/notebookEditor';
import URI from 'vs/base/common/uri';
let counter = 0; let counter = 0;
@@ -27,7 +26,7 @@ let counter = 0;
export class OpenNotebookAction extends Action { export class OpenNotebookAction extends Action {
public static ID = 'OpenNotebookAction'; public static ID = 'OpenNotebookAction';
public static LABEL = nls.localize('OpenNotebookAction', 'Open Notebook editor'); public static LABEL = localize('OpenNotebookAction', 'Open Notebook editor');
constructor( constructor(
id: string, id: string,
@@ -68,3 +67,5 @@ actionRegistry.registerWorkbenchAction(
), ),
OpenNotebookAction.LABEL OpenNotebookAction.LABEL
); );

View File

@@ -12,3 +12,15 @@
border-width: 1px; border-width: 1px;
border-style: solid; 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'; 'use strict';
import { nb } from 'sqlops'; 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 { export class SessionManager implements nb.SessionManager {
private _sessionManager: nb.SessionManager;
constructor() {
}
public get isReady(): boolean { public get isReady(): boolean {
return this._sessionManager.isReady; return true;
} }
public get ready(): Thenable<void> { public get ready(): Thenable<void> {
return this._sessionManager.ready; return Promise.resolve();
} }
public get specs(): nb.IAllKernels { 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> { 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> { 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);
} }
} }