123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373 |
- // Copyright (c) Jupyter Development Team.
- // Distributed under the terms of the Modified BSD License.
- import {
- Kernel, Session
- } from '@jupyterlab/services';
- import {
- IterableOrArrayLike, each
- } from '@phosphor/algorithm';
- import {
- Widget
- } from '@phosphor/widgets';
- import {
- Dialog, showDialog
- } from '@jupyterlab/apputils';
- import {
- DocumentRegistry
- } from './registry';
- /**
- * An interface for selecting a new kernel.
- */
- export
- interface IKernelSelection {
- /**
- * The name of the current session.
- */
- name: string;
- /**
- * The kernel spec information.
- */
- specs: Kernel.ISpecModels;
- /**
- * The current running sessions.
- */
- sessions: IterableOrArrayLike<Session.IModel>;
- /**
- * The desired kernel language.
- */
- preferredLanguage: string;
- /**
- * The optional existing kernel model.
- */
- kernel?: Kernel.IModel;
- /**
- * The host widget to be selected after the dialog is shown.
- */
- host?: Widget;
- }
- /**
- * An interface for populating a kernel selector.
- */
- export
- interface IPopulateOptions {
- /**
- * The Kernel specs.
- */
- specs: Kernel.ISpecModels;
- /**
- * The current running sessions.
- */
- sessions: IterableOrArrayLike<Session.IModel>;
- /**
- * The optional existing kernel model.
- */
- kernel?: Kernel.IModel;
- /**
- * The preferred kernel name.
- */
- preferredKernel?: string;
- /**
- * The preferred kernel language.
- */
- preferredLanguage?: string;
- }
- /**
- * Bring up a dialog to select a kernel.
- */
- export
- function selectKernel(options: IKernelSelection): Promise<Kernel.IModel> {
- let { specs, kernel, sessions, preferredLanguage } = options;
- // Create the dialog body.
- let body = document.createElement('div');
- let text = document.createElement('label');
- text.innerHTML = `Select kernel for: "${options.name}"`;
- body.appendChild(text);
- let selector = document.createElement('select');
- body.appendChild(selector);
- // Get the current sessions, populate the kernels, and show the dialog.
- populateKernels(selector, { specs, sessions, preferredLanguage, kernel });
- let select = Dialog.okButton({ label: 'SELECT' });
- return showDialog({
- title: 'Select Kernel',
- body,
- buttons: [Dialog.cancelButton(), select]
- }).then(result => {
- // Change the kernel if a kernel was selected.
- if (result.accept) {
- return JSON.parse(selector.value) as Kernel.IModel;
- }
- return void 0;
- });
- }
- /**
- * Change the kernel on a context.
- */
- export
- function selectKernelForContext(context: DocumentRegistry.Context, manager: Session.IManager, host?: Widget): Promise<void> {
- return manager.ready.then(() => {
- let options: IKernelSelection = {
- name: context.path.split('/').pop(),
- specs: manager.specs,
- sessions: manager.running(),
- preferredLanguage: context.model.defaultKernelLanguage,
- kernel: context.kernel ? context.kernel.model : null,
- };
- return selectKernel(options);
- }).then(kernel => {
- if (host) {
- host.activate();
- }
- if (kernel && (kernel.id || kernel.name)) {
- context.changeKernel(kernel);
- } else if (kernel && !kernel.id && !kernel.name) {
- context.changeKernel();
- }
- });
- }
- /**
- * Get the appropriate kernel name.
- */
- export
- function findKernel(kernelName: string, language: string, specs: Kernel.ISpecModels): string {
- if (kernelName === 'unknown') {
- return specs.default;
- }
- // Look for an exact match.
- for (let specName in specs.kernelspecs) {
- if (specName === kernelName) {
- return kernelName;
- }
- }
- // Next try to match the language name.
- if (language === 'unknown') {
- return specs.default;
- }
- for (let specName in specs.kernelspecs) {
- let kernelLanguage = specs.kernelspecs[specName].language;
- if (language === kernelLanguage) {
- console.log('No exact match found for ' + specName +
- ', using kernel ' + specName + ' that matches ' +
- 'language=' + language);
- return specName;
- }
- }
- // Finally, use the default kernel.
- if (kernelName) {
- console.log(`No matching kernel found for ${kernelName}, ` +
- `using default kernel ${specs.default}`);
- }
- return specs.default;
- }
- /**
- * Populate a kernel dropdown list.
- *
- * @param node - The host node.
- *
- * @param options - The options used to populate the kernels.
- *
- * #### Notes
- * Populates the list with separated sections:
- * - Kernels matching the preferred language (display names).
- * - "None" signifying no kernel.
- * - The remaining kernels.
- * - Sessions matching the preferred language (file names).
- * - The remaining sessions.
- * If no preferred language is given or no kernels are found using
- * the preferred language, the default kernel is used in the first
- * section. Kernels are sorted by display name. Sessions display the
- * base name of the file with an ellipsis overflow and a tooltip with
- * the explicit session information.
- */
- export
- function populateKernels(node: HTMLSelectElement, options: IPopulateOptions): void {
- // Clear any existing options.
- while (node.firstChild) {
- node.removeChild(node.firstChild);
- }
- let maxLength = 10;
- let { preferredKernel, preferredLanguage, sessions, specs, kernel } = options;
- let existing = kernel ? kernel.id : void 0;
- // Create mappings of display names and languages for kernel name.
- let displayNames: { [key: string]: string } = Object.create(null);
- let languages: { [key: string]: string } = Object.create(null);
- for (let name in specs.kernelspecs) {
- let spec = specs.kernelspecs[name];
- displayNames[name] = spec.display_name;
- maxLength = Math.max(maxLength, displayNames[name].length);
- languages[name] = spec.language;
- }
- // Handle a kernel by name.
- let names: string[] = [];
- if (preferredKernel && preferredKernel in specs.kernelspecs) {
- names.push(name);
- }
- // Handle a preferred kernel language in order of display name.
- let preferred = document.createElement('optgroup');
- preferred.label = 'Start Preferred Kernel';
- if (preferredLanguage && specs) {
- for (let name in specs.kernelspecs) {
- if (languages[name] === preferredLanguage) {
- names.push(name);
- }
- }
- names.sort((a, b) => displayNames[a].localeCompare(displayNames[b]));
- for (let name of names) {
- preferred.appendChild(optionForName(name, displayNames[name]));
- }
- }
- // Use the default kernel if no preferred language or none were found.
- if (!names.length) {
- let name = specs.default;
- preferred.appendChild(optionForName(name, displayNames[name]));
- }
- if (preferred.firstChild) {
- node.appendChild(preferred);
- }
- // Add an option for no kernel
- node.appendChild(optionForNone());
- let other = document.createElement('optgroup');
- other.label = 'Start Other Kernel';
- // Add the rest of the kernel names in alphabetical order.
- let otherNames: string[] = [];
- for (let name in specs.kernelspecs) {
- if (names.indexOf(name) !== -1) {
- continue;
- }
- otherNames.push(name);
- }
- otherNames.sort((a, b) => displayNames[a].localeCompare(displayNames[b]));
- for (let name of otherNames) {
- other.appendChild(optionForName(name, displayNames[name]));
- }
- // Add a separator option if there were any other names.
- if (otherNames.length) {
- node.appendChild(other);
- }
- // Add the sessions using the preferred language first.
- let matchingSessions: Session.IModel[] = [];
- let otherSessions: Session.IModel[] = [];
- each(sessions, session => {
- if (preferredLanguage &&
- languages[session.kernel.name] === preferredLanguage &&
- session.kernel.id !== existing) {
- matchingSessions.push(session);
- } else if (session.kernel.id !== existing) {
- otherSessions.push(session);
- }
- });
- let matching = document.createElement('optgroup');
- matching.label = 'Use Kernel from Preferred Session';
- node.appendChild(matching);
- if (matchingSessions.length) {
- matchingSessions.sort((a, b) => {
- return a.notebook.path.localeCompare(b.notebook.path);
- });
- each(matchingSessions, session => {
- let name = displayNames[session.kernel.name];
- matching.appendChild(optionForSession(session, name, maxLength));
- });
- }
- let otherSessionsNode = document.createElement('optgroup');
- otherSessionsNode.label = 'Use Kernel from Other Session';
- node.appendChild(otherSessionsNode);
- if (otherSessions.length) {
- otherSessions.sort((a, b) => {
- return a.notebook.path.localeCompare(b.notebook.path);
- });
- each(otherSessions, session => {
- let name = displayNames[session.kernel.name] || session.kernel.name;
- otherSessionsNode.appendChild(optionForSession(session, name, maxLength));
- });
- }
- node.selectedIndex = 0;
- }
- /**
- * Create an option element for a kernel name.
- */
- function optionForName(name: string, displayName: string): HTMLOptionElement {
- let option = document.createElement('option');
- option.text = displayName;
- option.value = JSON.stringify({ name });
- return option;
- }
- /**
- * Create an option for no kernel.
- */
- function optionForNone(): HTMLOptGroupElement {
- let group = document.createElement('optgroup');
- group.label = 'Use No Kernel';
- let option = document.createElement('option');
- option.text = 'No Kernel';
- option.value = JSON.stringify({id: null, name: null});
- group.appendChild(option);
- return group;
- }
- /**
- * Create an option element for a session.
- */
- function optionForSession(session: Session.IModel, displayName: string, maxLength: number): HTMLOptionElement {
- let option = document.createElement('option');
- let sessionName = session.notebook.path.split('/').pop();
- const CONSOLE_REGEX = /^console-(\d)+-[0-9a-f]+$/;
- if (CONSOLE_REGEX.test(sessionName)) {
- sessionName = `Console ${sessionName.match(CONSOLE_REGEX)[1]}`;
- }
- if (sessionName.length > maxLength) {
- sessionName = sessionName.slice(0, maxLength - 3) + '...';
- }
- option.text = sessionName;
- option.value = JSON.stringify({ id: session.kernel.id });
- option.title = `Path: ${session.notebook.path}\n` +
- `Kernel Name: ${displayName}\n` +
- `Kernel Id: ${session.kernel.id}`;
- return option;
- }
|