UNPKG

@jupyterlab/apputils

Version:
1,161 lines 40.9 kB
// Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. import { PathExt } from '@jupyterlab/coreutils'; import { nullTranslator } from '@jupyterlab/translation'; import { find } from '@lumino/algorithm'; import { JSONExt, PromiseDelegate, UUID } from '@lumino/coreutils'; import { Signal } from '@lumino/signaling'; import { Widget } from '@lumino/widgets'; import * as React from 'react'; import { Dialog, showDialog } from './dialog'; /** * The default implementation for a session context object. */ export class SessionContext { /** * Construct a new session context. */ constructor(options) { var _a, _b, _c, _d; this._path = ''; this._name = ''; this._type = ''; this._prevKernelName = ''; this._isDisposed = false; this._disposed = new Signal(this); this._session = null; this._ready = new PromiseDelegate(); this._initializing = false; this._initStarted = new PromiseDelegate(); this._initPromise = new PromiseDelegate(); this._isReady = false; this._isTerminating = false; this._isRestarting = false; this._kernelChanged = new Signal(this); this._preferenceChanged = new Signal(this); this._sessionChanged = new Signal(this); this._statusChanged = new Signal(this); this._connectionStatusChanged = new Signal(this); this._pendingInput = false; this._iopubMessage = new Signal(this); this._unhandledMessage = new Signal(this); this._propertyChanged = new Signal(this); this._dialog = null; this._busyDisposable = null; this._pendingKernelName = ''; this._pendingSessionRequest = ''; this.sessionManager = options.sessionManager; this.specsManager = options.specsManager; this.translator = options.translator || nullTranslator; this._trans = this.translator.load('jupyterlab'); this._path = (_a = options.path) !== null && _a !== void 0 ? _a : UUID.uuid4(); this._type = (_b = options.type) !== null && _b !== void 0 ? _b : ''; this._name = (_c = options.name) !== null && _c !== void 0 ? _c : ''; this._setBusy = options.setBusy; this._kernelPreference = (_d = options.kernelPreference) !== null && _d !== void 0 ? _d : {}; } /** * The current session connection. */ get session() { var _a; return (_a = this._session) !== null && _a !== void 0 ? _a : null; } /** * The session path. * * #### Notes * Typically `.session.path` should be used. This attribute is useful if * there is no current session. */ get path() { return this._path; } /** * The session type. * * #### Notes * Typically `.session.type` should be used. This attribute is useful if * there is no current session. */ get type() { return this._type; } /** * The session name. * * #### Notes * Typically `.session.name` should be used. This attribute is useful if * there is no current session. */ get name() { return this._name; } /** * A signal emitted when the kernel connection changes, proxied from the session connection. */ get kernelChanged() { return this._kernelChanged; } /** * A signal emitted when the session connection changes. */ get sessionChanged() { return this._sessionChanged; } /** * A signal emitted when the kernel status changes, proxied from the kernel. */ get statusChanged() { return this._statusChanged; } /** * A flag indicating if the session has pending input, proxied from the kernel. */ get pendingInput() { return this._pendingInput; } /** * A signal emitted when the kernel status changes, proxied from the kernel. */ get connectionStatusChanged() { return this._connectionStatusChanged; } /** * A signal emitted for iopub kernel messages, proxied from the kernel. */ get iopubMessage() { return this._iopubMessage; } /** * A signal emitted for an unhandled kernel message, proxied from the kernel. */ get unhandledMessage() { return this._unhandledMessage; } /** * A signal emitted when a session property changes, proxied from the current session. */ get propertyChanged() { return this._propertyChanged; } /** * The kernel preference of this client session. * * This is used when selecting a new kernel, and should reflect the sort of * kernel the activity prefers. */ get kernelPreference() { return this._kernelPreference; } set kernelPreference(value) { if (!JSONExt.deepEqual(value, this._kernelPreference)) { const oldValue = this._kernelPreference; this._kernelPreference = value; this._preferenceChanged.emit({ name: 'kernelPreference', oldValue, newValue: JSONExt.deepCopy(value) }); } } /** * Signal emitted if the kernel preference changes. */ get kernelPreferenceChanged() { return this._preferenceChanged; } /** * Whether the context is ready. */ get isReady() { return this._isReady; } /** * A promise that is fulfilled when the context is ready. */ get ready() { return this._ready.promise; } /** * Whether the context is terminating. */ get isTerminating() { return this._isTerminating; } /** * Whether the context is restarting. */ get isRestarting() { return this._isRestarting; } /** * Whether the kernel is "No Kernel" or not. * * #### Notes * As the displayed name is translated, this can be used directly. */ get hasNoKernel() { return this.kernelDisplayName === this.noKernelName; } /** * The display name of the current kernel, or a sensible alternative. * * #### Notes * This is a convenience function to have a consistent sensible name for the * kernel. */ get kernelDisplayName() { var _a, _b, _c, _d, _e, _f, _g; const kernel = (_a = this.session) === null || _a === void 0 ? void 0 : _a.kernel; if (this._pendingKernelName === this.noKernelName) { return this.noKernelName; } if (this._pendingKernelName) { return ((_d = (_c = (_b = this.specsManager.specs) === null || _b === void 0 ? void 0 : _b.kernelspecs[this._pendingKernelName]) === null || _c === void 0 ? void 0 : _c.display_name) !== null && _d !== void 0 ? _d : this._pendingKernelName); } if (!kernel) { return this.noKernelName; } return ((_g = (_f = (_e = this.specsManager.specs) === null || _e === void 0 ? void 0 : _e.kernelspecs[kernel.name]) === null || _f === void 0 ? void 0 : _f.display_name) !== null && _g !== void 0 ? _g : kernel.name); } /** * A sensible status to display * * #### Notes * This combines the status and connection status into a single status for * the user. */ get kernelDisplayStatus() { var _a, _b; const kernel = (_a = this.session) === null || _a === void 0 ? void 0 : _a.kernel; if (this._isTerminating) { return 'terminating'; } if (this._isRestarting) { return 'restarting'; } if (this._pendingKernelName === this.noKernelName) { return 'unknown'; } if (!kernel && this._pendingKernelName) { return 'initializing'; } if (!kernel && !this.isReady && this.kernelPreference.canStart !== false && this.kernelPreference.shouldStart !== false) { return 'initializing'; } return ((_b = ((kernel === null || kernel === void 0 ? void 0 : kernel.connectionStatus) === 'connected' ? kernel === null || kernel === void 0 ? void 0 : kernel.status : kernel === null || kernel === void 0 ? void 0 : kernel.connectionStatus)) !== null && _b !== void 0 ? _b : 'unknown'); } /** * The name of the previously started kernel. */ get prevKernelName() { return this._prevKernelName; } /** * Test whether the context is disposed. */ get isDisposed() { return this._isDisposed; } /** * A signal emitted when the poll is disposed. */ get disposed() { return this._disposed; } /** * Get the constant displayed name for "No Kernel" */ get noKernelName() { return this._trans.__('No Kernel'); } /** * Dispose of the resources held by the context. */ dispose() { if (this._isDisposed) { return; } this._isDisposed = true; this._disposed.emit(); if (this._session) { if (this.kernelPreference.shutdownOnDispose) { // Fire and forget the session shutdown request this.sessionManager.shutdown(this._session.id).catch(reason => { console.error(`Kernel not shut down ${reason}`); }); } // Dispose the session connection this._session.dispose(); this._session = null; } if (this._dialog) { this._dialog.dispose(); } if (this._busyDisposable) { this._busyDisposable.dispose(); this._busyDisposable = null; } Signal.clearData(this); } /** * Starts new Kernel. * * @returns Whether to ask the user to pick a kernel. */ async startKernel() { const preference = this.kernelPreference; if (!preference.autoStartDefault && preference.shouldStart === false) { return true; } let options; if (preference.id) { options = { id: preference.id }; } else { const name = Private.getDefaultKernel({ specs: this.specsManager.specs, sessions: this.sessionManager.running(), preference }); if (name) { options = { name }; } } if (options) { try { await this._changeKernel(options); return false; } catch (err) { /* no-op */ } } // Always fall back to selecting a kernel return true; } /** * Restart the current Kernel. * * @returns A promise that resolves when the kernel is restarted. */ async restartKernel() { var _a, _b, _c, _d, _e, _f; const kernel = ((_a = this.session) === null || _a === void 0 ? void 0 : _a.kernel) || null; if (this._isRestarting) { return; } this._isRestarting = true; this._isReady = false; this._statusChanged.emit('restarting'); try { await ((_c = (_b = this.session) === null || _b === void 0 ? void 0 : _b.kernel) === null || _c === void 0 ? void 0 : _c.restart()); this._isReady = true; } catch (e) { console.error(e); } this._isRestarting = false; this._statusChanged.emit(((_e = (_d = this.session) === null || _d === void 0 ? void 0 : _d.kernel) === null || _e === void 0 ? void 0 : _e.status) || 'unknown'); this._kernelChanged.emit({ name: 'kernel', oldValue: kernel, newValue: ((_f = this.session) === null || _f === void 0 ? void 0 : _f.kernel) || null }); } /** * Change the current kernel associated with the session. */ async changeKernel(options = {}) { if (this.isDisposed) { throw new Error('Disposed'); } // Wait for the initialization method to try // and start its kernel first to ensure consistent // ordering. await this._initStarted.promise; return this._changeKernel(options); } /** * Kill the kernel and shutdown the session. * * @returns A promise that resolves when the session is shut down. */ async shutdown() { if (this.isDisposed || !this._initializing) { return; } await this._initStarted.promise; this._pendingSessionRequest = ''; this._pendingKernelName = this.noKernelName; return this._shutdownSession(); } /** * Initialize the session context * * @returns A promise that resolves with whether to ask the user to select a kernel. * * #### Notes * If a server session exists on the current path, we will connect to it. * If preferences include disabling `canStart` or `shouldStart`, no * server session will be started. * If a kernel id is given, we attempt to start a session with that id. * If a default kernel is available, we connect to it. * Otherwise we ask the user to select a kernel. */ async initialize() { if (this._initializing) { return this._initPromise.promise; } this._initializing = true; const needsSelection = await this._initialize(); if (!needsSelection) { this._isReady = true; this._ready.resolve(undefined); } if (!this._pendingSessionRequest) { this._initStarted.resolve(void 0); } this._initPromise.resolve(needsSelection); return needsSelection; } /** * Inner initialize function that doesn't handle promises. * This makes it easier to consolidate promise handling logic. */ async _initialize() { const manager = this.sessionManager; await manager.ready; await manager.refreshRunning(); const model = find(manager.running(), item => { return item.path === this._path; }); if (model) { try { const session = manager.connectTo({ model }); this._handleNewSession(session); } catch (err) { void this._handleSessionError(err); return Promise.reject(err); } } return await this._startIfNecessary(); } /** * Shut down the current session. */ async _shutdownSession() { var _a; const session = this._session; // Capture starting values in case an error is raised. const isTerminating = this._isTerminating; const isReady = this._isReady; this._isTerminating = true; this._isReady = false; this._statusChanged.emit('terminating'); try { await (session === null || session === void 0 ? void 0 : session.shutdown()); this._isTerminating = false; session === null || session === void 0 ? void 0 : session.dispose(); this._session = null; const kernel = (session === null || session === void 0 ? void 0 : session.kernel) || null; this._statusChanged.emit('unknown'); this._kernelChanged.emit({ name: 'kernel', oldValue: kernel, newValue: null }); this._sessionChanged.emit({ name: 'session', oldValue: session, newValue: null }); } catch (err) { this._isTerminating = isTerminating; this._isReady = isReady; const status = (_a = session === null || session === void 0 ? void 0 : session.kernel) === null || _a === void 0 ? void 0 : _a.status; if (status === undefined) { this._statusChanged.emit('unknown'); } else { this._statusChanged.emit(status); } throw err; } return; } /** * Start the session if necessary. * * @returns Whether to ask the user to pick a kernel. */ async _startIfNecessary() { var _a; const preference = this.kernelPreference; if (this.isDisposed || ((_a = this.session) === null || _a === void 0 ? void 0 : _a.kernel) || preference.shouldStart === false || preference.canStart === false) { // Not necessary to start a kernel return false; } return this.startKernel(); } /** * Change the kernel. */ async _changeKernel(model = {}) { if (model.name) { this._pendingKernelName = model.name; } if (!this._session) { this._kernelChanged.emit({ name: 'kernel', oldValue: null, newValue: null }); } // Guarantee that the initialized kernel // will be started first. if (!this._pendingSessionRequest) { this._initStarted.resolve(void 0); } // If we already have a session, just change the kernel. if (this._session && !this._isTerminating) { try { await this._session.changeKernel(model); return this._session.kernel; } catch (err) { void this._handleSessionError(err); throw err; } } // Use a UUID for the path to overcome a race condition on the server // where it will re-use a session for a given path but only after // the kernel finishes starting. // We later switch to the real path below. // Use the correct directory so the kernel will be started in that directory. const dirName = PathExt.dirname(this._path); const requestId = (this._pendingSessionRequest = PathExt.join(dirName, UUID.uuid4())); try { this._statusChanged.emit('starting'); const session = await this.sessionManager.startNew({ path: requestId, type: this._type, name: this._name, kernel: model }); // Handle a preempt. if (this._pendingSessionRequest !== session.path) { await session.shutdown(); session.dispose(); return null; } // Change to the real path. await session.setPath(this._path); // Update the name in case it has changed since we launched the session. await session.setName(this._name); if (this._session && !this._isTerminating) { await this._shutdownSession(); } return this._handleNewSession(session); } catch (err) { void this._handleSessionError(err); throw err; } } /** * Handle a new session object. */ _handleNewSession(session) { var _a, _b, _c; if (this.isDisposed) { throw Error('Disposed'); } if (!this._isReady) { this._isReady = true; this._ready.resolve(undefined); } if (this._session) { this._session.dispose(); } this._session = session; this._pendingKernelName = ''; if (session) { this._prevKernelName = (_b = (_a = session.kernel) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : ''; session.disposed.connect(this._onSessionDisposed, this); session.propertyChanged.connect(this._onPropertyChanged, this); session.kernelChanged.connect(this._onKernelChanged, this); session.statusChanged.connect(this._onStatusChanged, this); session.connectionStatusChanged.connect(this._onConnectionStatusChanged, this); session.pendingInput.connect(this._onPendingInput, this); session.iopubMessage.connect(this._onIopubMessage, this); session.unhandledMessage.connect(this._onUnhandledMessage, this); if (session.path !== this._path) { this._onPropertyChanged(session, 'path'); } if (session.name !== this._name) { this._onPropertyChanged(session, 'name'); } if (session.type !== this._type) { this._onPropertyChanged(session, 'type'); } } // Any existing session/kernel connection was disposed above when the session was // disposed, so the oldValue should be null. this._sessionChanged.emit({ name: 'session', oldValue: null, newValue: session }); this._kernelChanged.emit({ oldValue: null, newValue: (session === null || session === void 0 ? void 0 : session.kernel) || null, name: 'kernel' }); this._statusChanged.emit(((_c = session === null || session === void 0 ? void 0 : session.kernel) === null || _c === void 0 ? void 0 : _c.status) || 'unknown'); return (session === null || session === void 0 ? void 0 : session.kernel) || null; } /** * Handle an error in session startup. */ async _handleSessionError(err) { this._handleNewSession(null); let traceback = ''; let message = ''; try { traceback = err.traceback; message = err.message; } catch (err) { // no-op } await this._displayKernelError(message, traceback); } /** * Display kernel error */ async _displayKernelError(message, traceback) { const body = (React.createElement("div", null, message && React.createElement("pre", null, message), traceback && (React.createElement("details", { className: "jp-mod-wide" }, React.createElement("pre", null, traceback))))); const dialog = (this._dialog = new Dialog({ title: this._trans.__('Error Starting Kernel'), body, buttons: [Dialog.okButton()] })); await dialog.launch(); this._dialog = null; } /** * Handle a session termination. */ _onSessionDisposed() { if (this._session) { const oldValue = this._session; this._session = null; const newValue = this._session; this._sessionChanged.emit({ name: 'session', oldValue, newValue }); } } /** * Handle a change to a session property. */ _onPropertyChanged(sender, property) { switch (property) { case 'path': this._path = sender.path; break; case 'name': this._name = sender.name; break; case 'type': this._type = sender.type; break; default: throw new Error(`unrecognized property ${property}`); } this._propertyChanged.emit(property); } /** * Handle a change to the kernel. */ _onKernelChanged(sender, args) { this._kernelChanged.emit(args); } /** * Handle a change to the session status. */ _onStatusChanged(sender, status) { var _a; if (status === 'dead') { const model = (_a = sender.kernel) === null || _a === void 0 ? void 0 : _a.model; if (model === null || model === void 0 ? void 0 : model.reason) { const traceback = model.traceback || ''; void this._displayKernelError(model.reason, traceback); } } // Set that this kernel is busy, if we haven't already // If we have already, and now we aren't busy, dispose // of the busy disposable. if (this._setBusy) { if (status === 'busy') { if (!this._busyDisposable) { this._busyDisposable = this._setBusy(); } } else { if (this._busyDisposable) { this._busyDisposable.dispose(); this._busyDisposable = null; } } } // Proxy the signal this._statusChanged.emit(status); } /** * Handle a change to the session status. */ _onConnectionStatusChanged(sender, status) { // Proxy the signal this._connectionStatusChanged.emit(status); } /** * Handle a change to the pending input. */ _onPendingInput(sender, value) { // Set the signal value this._pendingInput = value; } /** * Handle an iopub message. */ _onIopubMessage(sender, message) { if (message.header.msg_type === 'shutdown_reply') { this.session.kernel.removeInputGuard(); } this._iopubMessage.emit(message); } /** * Handle an unhandled message. */ _onUnhandledMessage(sender, message) { this._unhandledMessage.emit(message); } } /** * A namespace for `SessionContext` statics. */ (function (SessionContext) { /** * Get the default kernel name given select options. */ function getDefaultKernel(options) { const { preference } = options; const { shouldStart } = preference; if (shouldStart === false) { return null; } return Private.getDefaultKernel(options); } SessionContext.getDefaultKernel = getDefaultKernel; })(SessionContext || (SessionContext = {})); /** * The default implementation of the client session dialog provider. */ export class SessionContextDialogs { constructor(options = {}) { var _a; this._translator = (_a = options.translator) !== null && _a !== void 0 ? _a : nullTranslator; } /** * Select a kernel for the session. */ async selectKernel(sessionContext) { if (sessionContext.isDisposed) { return Promise.resolve(); } const trans = this._translator.load('jupyterlab'); // If there is no existing kernel, offer the option // to keep no kernel. let label = trans.__('Cancel'); if (sessionContext.hasNoKernel) { label = sessionContext.kernelDisplayName; } const buttons = [ Dialog.cancelButton({ label }), Dialog.okButton({ label: trans.__('Select'), ariaLabel: trans.__('Select Kernel') }) ]; const autoStartDefault = sessionContext.kernelPreference.autoStartDefault; const hasCheckbox = typeof autoStartDefault === 'boolean'; const dialog = new Dialog({ title: trans.__('Select Kernel'), body: new Private.KernelSelector(sessionContext, this._translator), buttons, checkbox: hasCheckbox ? { label: trans.__('Always start the preferred kernel'), caption: trans.__('Remember my choice and always start the preferred kernel'), checked: autoStartDefault } : null }); const result = await dialog.launch(); if (sessionContext.isDisposed || !result.button.accept) { return; } if (hasCheckbox && result.isChecked !== null) { sessionContext.kernelPreference = { ...sessionContext.kernelPreference, autoStartDefault: result.isChecked }; } const model = result.value; if (model === null && !sessionContext.hasNoKernel) { return sessionContext.shutdown(); } if (model) { await sessionContext.changeKernel(model); } } /** * Restart the session. * * @returns A promise that resolves with whether the kernel has restarted. * * #### Notes * If there is a running kernel, present a dialog. * If there is no kernel, we start a kernel with the last run * kernel name and resolves with `true`. */ async restart(sessionContext) { var _a; const trans = this._translator.load('jupyterlab'); await sessionContext.initialize(); if (sessionContext.isDisposed) { throw new Error('session already disposed'); } const kernel = (_a = sessionContext.session) === null || _a === void 0 ? void 0 : _a.kernel; if (!kernel && sessionContext.prevKernelName) { await sessionContext.changeKernel({ name: sessionContext.prevKernelName }); return true; } // Bail if there is no previous kernel to start. if (!kernel) { throw new Error('No kernel to restart'); } const restartBtn = Dialog.warnButton({ label: trans.__('Restart'), ariaLabel: trans.__('Confirm Kernel Restart') }); const result = await showDialog({ title: trans.__('Restart Kernel?'), body: trans.__('Do you want to restart the kernel of %1? All variables will be lost.', sessionContext.name), buttons: [ Dialog.cancelButton({ ariaLabel: trans.__('Cancel Kernel Restart') }), restartBtn ] }); if (kernel.isDisposed) { return false; } if (result.button.accept) { await sessionContext.restartKernel(); return true; } return false; } } /** * The namespace for module private data. */ var Private; (function (Private) { /** * A widget that provides a kernel selection. */ class KernelSelector extends Widget { /** * Create a new kernel selector widget. */ constructor(sessionContext, translator) { super({ node: createSelectorNode(sessionContext, translator) }); } /** * Get the value of the kernel selector widget. */ getValue() { const selector = this.node.querySelector('select'); return JSON.parse(selector.value); } } Private.KernelSelector = KernelSelector; /** * Create a node for a kernel selector widget. */ function createSelectorNode(sessionContext, translator) { // Create the dialog body. translator = translator || nullTranslator; const trans = translator.load('jupyterlab'); const body = document.createElement('div'); const text = document.createElement('label'); text.textContent = `${trans.__('Select kernel for:')} "${sessionContext.name}"`; body.appendChild(text); const options = getKernelSearch(sessionContext); const selector = document.createElement('select'); populateKernelSelect(selector, options, translator, !sessionContext.hasNoKernel ? sessionContext.kernelDisplayName : null); body.appendChild(selector); return body; } /** * Get the default kernel name given select options. */ function getDefaultKernel(options) { var _a; const { specs, preference } = options; const { name, language, canStart, autoStartDefault } = preference; if (!specs || canStart === false) { return null; } const defaultName = autoStartDefault ? specs.default : null; if (!name && !language) { return defaultName; } // Look for an exact match of a spec name. for (const specName in specs.kernelspecs) { if (specName === name) { return name; } } // Bail if there is no language. if (!language) { return defaultName; } // Check for a single kernel matching the language. const matches = []; for (const specName in specs.kernelspecs) { const kernelLanguage = (_a = specs.kernelspecs[specName]) === null || _a === void 0 ? void 0 : _a.language; if (language === kernelLanguage) { matches.push(specName); } } if (matches.length === 1) { const specName = matches[0]; console.warn('No exact match found for ' + specName + ', using kernel ' + specName + ' that matches ' + 'language=' + language); return specName; } // No matches found. return defaultName; } Private.getDefaultKernel = getDefaultKernel; /** * Populate a kernel select node for the session. */ function populateKernelSelect(node, options, translator, currentKernelDisplayName = null) { var _a; while (node.firstChild) { node.removeChild(node.firstChild); } const { preference, sessions, specs } = options; const { name, id, language, canStart, shouldStart } = preference; translator = translator || nullTranslator; const trans = translator.load('jupyterlab'); if (!specs || canStart === false) { node.appendChild(optionForNone(translator)); node.value = 'null'; node.disabled = true; return; } node.disabled = false; // Create mappings of display names and languages for kernel name. const displayNames = Object.create(null); const languages = Object.create(null); for (const name in specs.kernelspecs) { const spec = specs.kernelspecs[name]; displayNames[name] = spec.display_name; languages[name] = spec.language; } // Handle a kernel by name. const names = []; if (name && name in specs.kernelspecs) { names.push(name); } // Then look by language if we have a selected and existing kernel. if (name && names.length > 0 && language) { for (const specName in specs.kernelspecs) { if (name !== specName && languages[specName] === language) { names.push(specName); } } } // Use the default kernel if no kernels were found. if (!names.length) { names.push(specs.default); } // Handle a preferred kernels in order of display name. const preferred = document.createElement('optgroup'); preferred.label = trans.__('Start Preferred Kernel'); names.sort((a, b) => displayNames[a].localeCompare(displayNames[b])); for (const name of names) { preferred.appendChild(optionForName(name, displayNames[name])); } if (preferred.firstChild) { node.appendChild(preferred); } // Add an option for no kernel node.appendChild(optionForNone(translator)); const other = document.createElement('optgroup'); other.label = trans.__('Start Other Kernel'); // Add the rest of the kernel names in alphabetical order. const otherNames = []; for (const specName in specs.kernelspecs) { if (names.indexOf(specName) !== -1) { continue; } otherNames.push(specName); } otherNames.sort((a, b) => displayNames[a].localeCompare(displayNames[b])); for (const otherName of otherNames) { other.appendChild(optionForName(otherName, displayNames[otherName])); } // Add a separator option if there were any other names. if (otherNames.length) { node.appendChild(other); } // Handle the default value. if (shouldStart === false) { node.value = 'null'; } else { let selectedIndex = 0; if (currentKernelDisplayName) { // Select current kernel by default. selectedIndex = [...node.options].findIndex(option => option.text === currentKernelDisplayName); selectedIndex = Math.max(selectedIndex, 0); } node.selectedIndex = selectedIndex; } // Bail if there are no sessions. if (!sessions) { return; } // Add the sessions using the preferred language first. const matchingSessions = []; const otherSessions = []; for (const session of sessions) { if (language && session.kernel && languages[session.kernel.name] === language && session.kernel.id !== id) { matchingSessions.push(session); } else if (((_a = session.kernel) === null || _a === void 0 ? void 0 : _a.id) !== id) { otherSessions.push(session); } } const matching = document.createElement('optgroup'); matching.label = trans.__('Use Kernel from Preferred Session'); node.appendChild(matching); if (matchingSessions.length) { matchingSessions.sort((a, b) => { return a.path.localeCompare(b.path); }); for (const session of matchingSessions) { const name = session.kernel ? displayNames[session.kernel.name] : ''; matching.appendChild(optionForSession(session, name, translator)); } } const otherSessionsNode = document.createElement('optgroup'); otherSessionsNode.label = trans.__('Use Kernel from Other Session'); node.appendChild(otherSessionsNode); if (otherSessions.length) { otherSessions.sort((a, b) => { return a.path.localeCompare(b.path); }); for (const session of otherSessions) { const name = session.kernel ? displayNames[session.kernel.name] || session.kernel.name : ''; otherSessionsNode.appendChild(optionForSession(session, name, translator)); } } } Private.populateKernelSelect = populateKernelSelect; /** * Get the kernel search options given a session context and session manager. */ function getKernelSearch(sessionContext) { return { specs: sessionContext.specsManager.specs, sessions: sessionContext.sessionManager.running(), preference: sessionContext.kernelPreference }; } /** * Create an option element for a kernel name. */ function optionForName(name, displayName) { const option = document.createElement('option'); option.text = displayName; option.value = JSON.stringify({ name }); return option; } /** * Create an option for no kernel. */ function optionForNone(translator) { translator = translator || nullTranslator; const trans = translator.load('jupyterlab'); const group = document.createElement('optgroup'); group.label = trans.__('Use No Kernel'); const option = document.createElement('option'); option.text = trans.__('No Kernel'); option.value = 'null'; group.appendChild(option); return group; } /** * Create an option element for a session. */ function optionForSession(session, displayName, translator) { var _a, _b; translator = translator || nullTranslator; const trans = translator.load('jupyterlab'); const option = document.createElement('option'); const sessionName = session.name || PathExt.basename(session.path); option.text = sessionName; option.value = JSON.stringify({ id: (_a = session.kernel) === null || _a === void 0 ? void 0 : _a.id }); option.title = `${trans.__('Path:')} ${session.path}\n` + `${trans.__('Name:')} ${sessionName}\n` + `${trans.__('Kernel Name:')} ${displayName}\n` + `${trans.__('Kernel Id:')} ${(_b = session.kernel) === null || _b === void 0 ? void 0 : _b.id}`; return option; } })(Private || (Private = {})); //# sourceMappingURL=sessioncontext.js.map