Path: blob/main/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelQuickPickStrategy.ts
5272 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { IAction } from '../../../../../base/common/actions.js';6import { groupBy } from '../../../../../base/common/arrays.js';7import { createCancelablePromise } from '../../../../../base/common/async.js';8import { CancellationToken } from '../../../../../base/common/cancellation.js';9import { Codicon } from '../../../../../base/common/codicons.js';10import { Event } from '../../../../../base/common/event.js';11import { DisposableStore } from '../../../../../base/common/lifecycle.js';12import { MarshalledId } from '../../../../../base/common/marshallingIds.js';13import { uppercaseFirstLetter } from '../../../../../base/common/strings.js';14import { Command } from '../../../../../editor/common/languages.js';15import { localize } from '../../../../../nls.js';16import { ICommandService } from '../../../../../platform/commands/common/commands.js';17import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js';18import { ILabelService } from '../../../../../platform/label/common/label.js';19import { ILogService } from '../../../../../platform/log/common/log.js';20import { IProductService } from '../../../../../platform/product/common/productService.js';21import { ProgressLocation } from '../../../../../platform/progress/common/progress.js';22import { IQuickInputButton, IQuickInputService, IQuickPick, IQuickPickItem, QuickPickInput } from '../../../../../platform/quickinput/common/quickInput.js';23import { ThemeIcon } from '../../../../../base/common/themables.js';24import { IExtension, IExtensionsWorkbenchService } from '../../../extensions/common/extensions.js';25import { IActiveNotebookEditor, INotebookExtensionRecommendation, JUPYTER_EXTENSION_ID, KERNEL_RECOMMENDATIONS } from '../notebookBrowser.js';26import { NotebookEditorWidget } from '../notebookEditorWidget.js';27import { executingStateIcon, selectKernelIcon } from '../notebookIcons.js';28import { NotebookTextModel } from '../../common/model/notebookTextModel.js';29import { INotebookKernel, INotebookKernelHistoryService, INotebookKernelMatchResult, INotebookKernelService, ISourceAction } from '../../common/notebookKernelService.js';30import { IExtensionService } from '../../../../services/extensions/common/extensions.js';31import { URI } from '../../../../../base/common/uri.js';32import { IOpenerService } from '../../../../../platform/opener/common/opener.js';33import { INotebookTextModel } from '../../common/notebookCommon.js';34import { SELECT_KERNEL_ID } from '../controller/coreActions.js';35import { EnablementState, IExtensionManagementServerService } from '../../../../services/extensionManagement/common/extensionManagement.js';36import { areSameExtensions } from '../../../../../platform/extensionManagement/common/extensionManagementUtil.js';3738type KernelPick = IQuickPickItem & { kernel: INotebookKernel };39function isKernelPick(item: QuickPickInput<IQuickPickItem>): item is KernelPick {40return 'kernel' in item;41}42type GroupedKernelsPick = IQuickPickItem & { kernels: INotebookKernel[]; source: string };43function isGroupedKernelsPick(item: QuickPickInput<IQuickPickItem>): item is GroupedKernelsPick {44return 'kernels' in item;45}46type SourcePick = IQuickPickItem & { action: ISourceAction };47function isSourcePick(item: QuickPickInput<IQuickPickItem>): item is SourcePick {48return 'action' in item;49}50type InstallExtensionPick = IQuickPickItem & { extensionIds: string[] };51function isInstallExtensionPick(item: QuickPickInput<IQuickPickItem>): item is InstallExtensionPick {52return item.id === 'installSuggested' && 'extensionIds' in item;53}54type SearchMarketplacePick = IQuickPickItem & { id: 'install' };55function isSearchMarketplacePick(item: QuickPickInput<IQuickPickItem>): item is SearchMarketplacePick {56return item.id === 'install';57}5859type KernelSourceQuickPickItem = IQuickPickItem & { command: Command; documentation?: string };60function isKernelSourceQuickPickItem(item: IQuickPickItem): item is KernelSourceQuickPickItem {61return 'command' in item;62}6364function supportAutoRun(item: QuickPickInput<IQuickPickItem>): item is IQuickPickItem {65return 'autoRun' in item && !!item.autoRun;66}67type KernelQuickPickItem = (IQuickPickItem & { autoRun?: boolean }) | SearchMarketplacePick | InstallExtensionPick | KernelPick | GroupedKernelsPick | SourcePick | KernelSourceQuickPickItem;68const KERNEL_PICKER_UPDATE_DEBOUNCE = 200;6970export type KernelQuickPickContext =71{ id: string; extension: string } |72{ notebookEditorId: string } |73{ id: string; extension: string; notebookEditorId: string } |74{ ui?: boolean; notebookEditor?: NotebookEditorWidget; skipIfAlreadySelected?: boolean };7576export interface IKernelPickerStrategy {77showQuickPick(editor: IActiveNotebookEditor, wantedKernelId?: string): Promise<boolean>;78}7980function toKernelQuickPick(kernel: INotebookKernel, selected: INotebookKernel | undefined) {81const res: KernelPick = {82kernel,83picked: kernel.id === selected?.id,84label: kernel.label,85description: kernel.description,86detail: kernel.detail87};88if (kernel.id === selected?.id) {89if (!res.description) {90res.description = localize('current1', "Currently Selected");91} else {92res.description = localize('current2', "{0} - Currently Selected", res.description);93}94}95return res;96}979899abstract class KernelPickerStrategyBase implements IKernelPickerStrategy {100constructor(101protected readonly _notebookKernelService: INotebookKernelService,102protected readonly _productService: IProductService,103protected readonly _quickInputService: IQuickInputService,104protected readonly _labelService: ILabelService,105protected readonly _logService: ILogService,106protected readonly _extensionWorkbenchService: IExtensionsWorkbenchService,107protected readonly _extensionService: IExtensionService,108protected readonly _commandService: ICommandService,109protected readonly _extensionManagementServerService: IExtensionManagementServerService110) { }111112async showQuickPick(editor: IActiveNotebookEditor, wantedId?: string, skipAutoRun?: boolean): Promise<boolean> {113const notebook = editor.textModel;114const scopedContextKeyService = editor.scopedContextKeyService;115const matchResult = this._getMatchingResult(notebook);116const { selected, all } = matchResult;117118let newKernel: INotebookKernel | undefined;119if (wantedId) {120for (const candidate of all) {121if (candidate.id === wantedId) {122newKernel = candidate;123break;124}125}126if (!newKernel) {127this._logService.warn(`wanted kernel DOES NOT EXIST, wanted: ${wantedId}, all: ${all.map(k => k.id)}`);128return false;129}130}131132if (newKernel) {133this._selecteKernel(notebook, newKernel);134return true;135}136137138const localDisposableStore = new DisposableStore();139const quickPick = localDisposableStore.add(this._quickInputService.createQuickPick<KernelQuickPickItem>({ useSeparators: true }));140const quickPickItems = this._getKernelPickerQuickPickItems(notebook, matchResult, this._notebookKernelService, scopedContextKeyService);141142if (quickPickItems.length === 1 && supportAutoRun(quickPickItems[0]) && !skipAutoRun) {143const picked = await this._handleQuickPick(editor, quickPickItems[0], quickPickItems as KernelQuickPickItem[]);144localDisposableStore.dispose();145return picked;146}147148quickPick.items = quickPickItems;149quickPick.canSelectMany = false;150quickPick.placeholder = selected151? localize('prompt.placeholder.change', "Change kernel for '{0}'", this._labelService.getUriLabel(notebook.uri, { relative: true }))152: localize('prompt.placeholder.select', "Select kernel for '{0}'", this._labelService.getUriLabel(notebook.uri, { relative: true }));153154quickPick.busy = this._notebookKernelService.getKernelDetectionTasks(notebook).length > 0;155156const kernelDetectionTaskListener = this._notebookKernelService.onDidChangeKernelDetectionTasks(() => {157quickPick.busy = this._notebookKernelService.getKernelDetectionTasks(notebook).length > 0;158});159160// run extension recommendataion task if quickPickItems is empty161const extensionRecommendataionPromise = quickPickItems.length === 0162? createCancelablePromise(token => this._showInstallKernelExtensionRecommendation(notebook, quickPick, this._extensionWorkbenchService, token))163: undefined;164165const kernelChangeEventListener = Event.debounce<void, void>(166Event.any(167this._notebookKernelService.onDidChangeSourceActions,168this._notebookKernelService.onDidAddKernel,169this._notebookKernelService.onDidRemoveKernel,170this._notebookKernelService.onDidChangeNotebookAffinity171),172(last, _current) => last,173KERNEL_PICKER_UPDATE_DEBOUNCE174)(async () => {175// reset quick pick progress176quickPick.busy = false;177extensionRecommendataionPromise?.cancel();178179const currentActiveItems = quickPick.activeItems;180const matchResult = this._getMatchingResult(notebook);181const quickPickItems = this._getKernelPickerQuickPickItems(notebook, matchResult, this._notebookKernelService, scopedContextKeyService);182quickPick.keepScrollPosition = true;183184// recalcuate active items185const activeItems: KernelQuickPickItem[] = [];186for (const item of currentActiveItems) {187if (isKernelPick(item)) {188const kernelId = item.kernel.id;189const sameItem = quickPickItems.find(pi => isKernelPick(pi) && pi.kernel.id === kernelId) as KernelPick | undefined;190if (sameItem) {191activeItems.push(sameItem);192}193} else if (isSourcePick(item)) {194const sameItem = quickPickItems.find(pi => isSourcePick(pi) && pi.action.action.id === item.action.action.id) as SourcePick | undefined;195if (sameItem) {196activeItems.push(sameItem);197}198}199}200201quickPick.items = quickPickItems;202quickPick.activeItems = activeItems;203}, this);204205const pick = await new Promise<{ selected: KernelQuickPickItem | undefined; items: KernelQuickPickItem[] }>((resolve, reject) => {206localDisposableStore.add(quickPick.onDidAccept(() => {207const item = quickPick.selectedItems[0];208if (item) {209resolve({ selected: item, items: quickPick.items as KernelQuickPickItem[] });210} else {211resolve({ selected: undefined, items: quickPick.items as KernelQuickPickItem[] });212}213214quickPick.hide();215}));216217localDisposableStore.add(quickPick.onDidHide(() => {218kernelDetectionTaskListener.dispose();219kernelChangeEventListener.dispose();220quickPick.dispose();221resolve({ selected: undefined, items: quickPick.items as KernelQuickPickItem[] });222}));223quickPick.show();224});225226localDisposableStore.dispose();227228if (pick.selected) {229return await this._handleQuickPick(editor, pick.selected, pick.items);230}231232return false;233}234235protected _getMatchingResult(notebook: NotebookTextModel) {236return this._notebookKernelService.getMatchingKernel(notebook);237}238239protected abstract _getKernelPickerQuickPickItems(240notebookTextModel: NotebookTextModel,241matchResult: INotebookKernelMatchResult,242notebookKernelService: INotebookKernelService,243scopedContextKeyService: IContextKeyService244): QuickPickInput<KernelQuickPickItem>[];245246protected async _handleQuickPick(editor: IActiveNotebookEditor, pick: KernelQuickPickItem, quickPickItems: KernelQuickPickItem[]): Promise<boolean> {247if (isKernelPick(pick)) {248const newKernel = pick.kernel;249this._selecteKernel(editor.textModel, newKernel);250return true;251}252253// actions254if (isSearchMarketplacePick(pick)) {255await this._showKernelExtension(256this._extensionWorkbenchService,257this._extensionService,258this._extensionManagementServerService,259editor.textModel.viewType,260[]261);262// suggestedExtension must be defined for this option to be shown, but still check to make TS happy263} else if (isInstallExtensionPick(pick)) {264await this._showKernelExtension(265this._extensionWorkbenchService,266this._extensionService,267this._extensionManagementServerService,268editor.textModel.viewType,269pick.extensionIds,270this._productService.quality !== 'stable'271);272} else if (isSourcePick(pick)) {273// selected explicilty, it should trigger the execution?274pick.action.runAction();275}276277return true;278}279280protected _selecteKernel(notebook: NotebookTextModel, kernel: INotebookKernel) {281this._notebookKernelService.selectKernelForNotebook(kernel, notebook);282}283284protected async _showKernelExtension(285extensionWorkbenchService: IExtensionsWorkbenchService,286extensionService: IExtensionService,287extensionManagementServerService: IExtensionManagementServerService,288viewType: string,289extIds: string[],290isInsiders?: boolean291) {292// If extension id is provided attempt to install the extension as the user has requested the suggested ones be installed293const extensionsToInstall: IExtension[] = [];294const extensionsToInstallOnRemote: IExtension[] = [];295const extensionsToEnable: IExtension[] = [];296297for (const extId of extIds) {298const extension = (await extensionWorkbenchService.getExtensions([{ id: extId }], CancellationToken.None))[0];299if (extension.enablementState === EnablementState.DisabledGlobally || extension.enablementState === EnablementState.DisabledWorkspace || extension.enablementState === EnablementState.DisabledByEnvironment) {300extensionsToEnable.push(extension);301} else if (!extensionWorkbenchService.installed.some(e => areSameExtensions(e.identifier, extension.identifier))) {302// Install this extension only if it hasn't already been installed.303const canInstall = await extensionWorkbenchService.canInstall(extension);304if (canInstall === true) {305extensionsToInstall.push(extension);306}307} else if (extensionManagementServerService.remoteExtensionManagementServer) {308// already installed, check if it should be installed on remote since we are not getting any kernels or kernel providers.309if (extensionWorkbenchService.installed.some(e => areSameExtensions(e.identifier, extension.identifier) && e.server === extensionManagementServerService.remoteExtensionManagementServer)) {310// extension exists on remote server. should not happen311continue;312} else {313// extension doesn't exist on remote server314const canInstall = await extensionWorkbenchService.canInstall(extension);315if (canInstall) {316extensionsToInstallOnRemote.push(extension);317}318}319}320}321322if (extensionsToInstall.length || extensionsToEnable.length || extensionsToInstallOnRemote.length) {323await Promise.all([...extensionsToInstall.map(async extension => {324await extensionWorkbenchService.install(325extension,326{327installPreReleaseVersion: isInsiders ?? false,328context: { skipWalkthrough: true },329},330ProgressLocation.Notification331);332}), ...extensionsToEnable.map(async extension => {333switch (extension.enablementState) {334case EnablementState.DisabledWorkspace:335await extensionWorkbenchService.setEnablement([extension], EnablementState.EnabledWorkspace);336return;337case EnablementState.DisabledGlobally:338await extensionWorkbenchService.setEnablement([extension], EnablementState.EnabledGlobally);339return;340case EnablementState.DisabledByEnvironment:341await extensionWorkbenchService.setEnablement([extension], EnablementState.EnabledByEnvironment);342return;343default:344break;345}346}), ...extensionsToInstallOnRemote.map(async extension => {347await extensionWorkbenchService.installInServer(extension, this._extensionManagementServerService.remoteExtensionManagementServer!);348})]);349350await extensionService.activateByEvent(`onNotebook:${viewType}`);351return;352}353354const pascalCased = viewType.split(/[^a-z0-9]/ig).map(uppercaseFirstLetter).join('');355await extensionWorkbenchService.openSearch(`@tag:notebookKernel${pascalCased}`);356}357358private async _showInstallKernelExtensionRecommendation(359notebookTextModel: NotebookTextModel,360quickPick: IQuickPick<KernelQuickPickItem, { useSeparators: true }>,361extensionWorkbenchService: IExtensionsWorkbenchService,362token: CancellationToken363) {364quickPick.busy = true;365366const newQuickPickItems = await this._getKernelRecommendationsQuickPickItems(notebookTextModel, extensionWorkbenchService);367quickPick.busy = false;368369if (token.isCancellationRequested) {370return;371}372373if (newQuickPickItems && quickPick.items.length === 0) {374quickPick.items = newQuickPickItems;375}376}377378protected async _getKernelRecommendationsQuickPickItems(379notebookTextModel: NotebookTextModel,380extensionWorkbenchService: IExtensionsWorkbenchService,381): Promise<QuickPickInput<SearchMarketplacePick | InstallExtensionPick>[] | undefined> {382const quickPickItems: QuickPickInput<SearchMarketplacePick | InstallExtensionPick>[] = [];383384const language = this.getSuggestedLanguage(notebookTextModel);385const suggestedExtension: INotebookExtensionRecommendation | undefined = language ? this.getSuggestedKernelFromLanguage(notebookTextModel.viewType, language) : undefined;386if (suggestedExtension) {387await extensionWorkbenchService.queryLocal();388389const extensions = extensionWorkbenchService.installed.filter(e =>390(e.enablementState === EnablementState.EnabledByEnvironment || e.enablementState === EnablementState.EnabledGlobally || e.enablementState === EnablementState.EnabledWorkspace)391&& suggestedExtension.extensionIds.includes(e.identifier.id)392);393394if (extensions.length === suggestedExtension.extensionIds.length) {395// it's installed but might be detecting kernels396return undefined;397}398399// We have a suggested kernel, show an option to install it400quickPickItems.push({401id: 'installSuggested',402description: suggestedExtension.displayName ?? suggestedExtension.extensionIds.join(', '),403label: `$(${Codicon.lightbulb.id}) ` + localize('installSuggestedKernel', 'Install/Enable suggested extensions'),404extensionIds: suggestedExtension.extensionIds405} satisfies InstallExtensionPick);406}407// there is no kernel, show the install from marketplace408quickPickItems.push({409id: 'install',410label: localize('searchForKernels', "Browse marketplace for kernel extensions"),411} satisfies SearchMarketplacePick);412413return quickPickItems;414}415416/**417* Examine the most common language in the notebook418* @param notebookTextModel The notebook text model419* @returns What the suggested language is for the notebook. Used for kernal installing420*/421private getSuggestedLanguage(notebookTextModel: NotebookTextModel): string | undefined {422const metaData = notebookTextModel.metadata;423const language_info = (metaData?.metadata as Record<string, unknown>)?.language_info as Record<string, string> | undefined;424let suggestedKernelLanguage: string | undefined = language_info?.name;425// TODO how do we suggest multi language notebooks?426if (!suggestedKernelLanguage) {427const cellLanguages = notebookTextModel.cells.map(cell => cell.language).filter(language => language !== 'markdown');428// Check if cell languages is all the same429if (cellLanguages.length > 1) {430const firstLanguage = cellLanguages[0];431if (cellLanguages.every(language => language === firstLanguage)) {432suggestedKernelLanguage = firstLanguage;433}434}435}436return suggestedKernelLanguage;437}438439/**440* Given a language and notebook view type suggest a kernel for installation441* @param language The language to find a suggested kernel extension for442* @returns A recommednation object for the recommended extension, else undefined443*/444private getSuggestedKernelFromLanguage(viewType: string, language: string): INotebookExtensionRecommendation | undefined {445const recommendation = KERNEL_RECOMMENDATIONS.get(viewType)?.get(language);446return recommendation;447}448}449450export class KernelPickerMRUStrategy extends KernelPickerStrategyBase {451constructor(452@INotebookKernelService _notebookKernelService: INotebookKernelService,453@IProductService _productService: IProductService,454@IQuickInputService _quickInputService: IQuickInputService,455@ILabelService _labelService: ILabelService,456@ILogService _logService: ILogService,457@IExtensionsWorkbenchService _extensionWorkbenchService: IExtensionsWorkbenchService,458@IExtensionService _extensionService: IExtensionService,459@IExtensionManagementServerService _extensionManagementServerService: IExtensionManagementServerService,460@ICommandService _commandService: ICommandService,461@INotebookKernelHistoryService private readonly _notebookKernelHistoryService: INotebookKernelHistoryService,462@IOpenerService private readonly _openerService: IOpenerService463464) {465super(466_notebookKernelService,467_productService,468_quickInputService,469_labelService,470_logService,471_extensionWorkbenchService,472_extensionService,473_commandService,474_extensionManagementServerService,475);476}477478protected _getKernelPickerQuickPickItems(notebookTextModel: NotebookTextModel, matchResult: INotebookKernelMatchResult, notebookKernelService: INotebookKernelService, scopedContextKeyService: IContextKeyService): QuickPickInput<KernelQuickPickItem>[] {479const quickPickItems: QuickPickInput<KernelQuickPickItem>[] = [];480481if (matchResult.selected) {482const kernelItem = toKernelQuickPick(matchResult.selected, matchResult.selected);483quickPickItems.push(kernelItem);484}485486matchResult.suggestions.filter(kernel => kernel.id !== matchResult.selected?.id).map(kernel => toKernelQuickPick(kernel, matchResult.selected))487.forEach(kernel => {488quickPickItems.push(kernel);489});490491const shouldAutoRun = quickPickItems.length === 0;492493if (quickPickItems.length > 0) {494quickPickItems.push({495type: 'separator'496});497}498499// select another kernel quick pick500quickPickItems.push({501id: 'selectAnother',502label: localize('selectAnotherKernel.more', "Select Another Kernel..."),503autoRun: shouldAutoRun504});505506return quickPickItems;507}508509protected override _selecteKernel(notebook: NotebookTextModel, kernel: INotebookKernel): void {510const currentInfo = this._notebookKernelService.getMatchingKernel(notebook);511if (currentInfo.selected) {512// there is already a selected kernel513this._notebookKernelHistoryService.addMostRecentKernel(currentInfo.selected);514}515super._selecteKernel(notebook, kernel);516this._notebookKernelHistoryService.addMostRecentKernel(kernel);517}518519protected override _getMatchingResult(notebook: NotebookTextModel): INotebookKernelMatchResult {520const { selected, all } = this._notebookKernelHistoryService.getKernels(notebook);521const matchingResult = this._notebookKernelService.getMatchingKernel(notebook);522return {523selected: selected,524all: matchingResult.all,525suggestions: all,526hidden: []527};528}529530protected override async _handleQuickPick(editor: IActiveNotebookEditor, pick: KernelQuickPickItem, items: KernelQuickPickItem[]): Promise<boolean> {531if (pick.id === 'selectAnother') {532return this.displaySelectAnotherQuickPick(editor, items.length === 1 && items[0] === pick);533}534535return super._handleQuickPick(editor, pick, items);536}537538private async displaySelectAnotherQuickPick(editor: IActiveNotebookEditor, kernelListEmpty: boolean): Promise<boolean> {539const notebook: NotebookTextModel = editor.textModel;540const disposables = new DisposableStore();541const quickPick = disposables.add(this._quickInputService.createQuickPick<KernelQuickPickItem>({ useSeparators: true }));542const quickPickItem = await new Promise<KernelQuickPickItem | IQuickInputButton | undefined>(resolve => {543// select from kernel sources544quickPick.title = kernelListEmpty ? localize('select', "Select Kernel") : localize('selectAnotherKernel', "Select Another Kernel");545quickPick.placeholder = localize('selectKernel.placeholder', "Type to choose a kernel source");546quickPick.busy = true;547quickPick.buttons = [this._quickInputService.backButton];548quickPick.show();549550disposables.add(quickPick.onDidTriggerButton(button => {551if (button === this._quickInputService.backButton) {552resolve(button);553}554}));555disposables.add(quickPick.onDidTriggerItemButton(async (e) => {556if (isKernelSourceQuickPickItem(e.item) && e.item.documentation !== undefined) {557const uri = URI.isUri(e.item.documentation) ? URI.parse(e.item.documentation) : await this._commandService.executeCommand<URI>(e.item.documentation);558if (uri) {559void this._openerService.open(uri, { openExternal: true });560}561}562}));563disposables.add(quickPick.onDidAccept(async () => {564resolve(quickPick.selectedItems[0]);565}));566disposables.add(quickPick.onDidHide(() => {567resolve(undefined);568}));569570this._calculdateKernelSources(editor).then(quickPickItems => {571quickPick.items = quickPickItems;572if (quickPick.items.length > 0) {573quickPick.busy = false;574}575});576577disposables.add(Event.debounce<void, void>(578Event.any(579this._notebookKernelService.onDidChangeSourceActions,580this._notebookKernelService.onDidAddKernel,581this._notebookKernelService.onDidRemoveKernel582),583(last, _current) => last,584KERNEL_PICKER_UPDATE_DEBOUNCE585)(async () => {586quickPick.busy = true;587const quickPickItems = await this._calculdateKernelSources(editor);588quickPick.items = quickPickItems;589quickPick.busy = false;590}));591});592593quickPick.hide();594disposables.dispose();595596if (quickPickItem === this._quickInputService.backButton) {597return this.showQuickPick(editor, undefined, true);598}599600if (quickPickItem) {601const selectedKernelPickItem = quickPickItem as KernelQuickPickItem;602if (isKernelSourceQuickPickItem(selectedKernelPickItem)) {603try {604const selectedKernelId = await this._executeCommand<string>(notebook, selectedKernelPickItem.command);605if (selectedKernelId) {606const { all } = await this._getMatchingResult(notebook);607const kernel = all.find(kernel => kernel.id === `ms-toolsai.jupyter/${selectedKernelId}`);608if (kernel) {609await this._selecteKernel(notebook, kernel);610return true;611}612return true;613} else {614return this.displaySelectAnotherQuickPick(editor, false);615}616} catch (ex) {617return false;618}619} else if (isKernelPick(selectedKernelPickItem)) {620await this._selecteKernel(notebook, selectedKernelPickItem.kernel);621return true;622} else if (isGroupedKernelsPick(selectedKernelPickItem)) {623await this._selectOneKernel(notebook, selectedKernelPickItem.label, selectedKernelPickItem.kernels);624return true;625} else if (isSourcePick(selectedKernelPickItem)) {626// selected explicilty, it should trigger the execution?627try {628await selectedKernelPickItem.action.runAction();629return true;630} catch (ex) {631return false;632}633} else if (isSearchMarketplacePick(selectedKernelPickItem)) {634await this._showKernelExtension(635this._extensionWorkbenchService,636this._extensionService,637this._extensionManagementServerService,638editor.textModel.viewType,639[]640);641return true;642} else if (isInstallExtensionPick(selectedKernelPickItem)) {643await this._showKernelExtension(644this._extensionWorkbenchService,645this._extensionService,646this._extensionManagementServerService,647editor.textModel.viewType,648selectedKernelPickItem.extensionIds,649this._productService.quality !== 'stable'650);651return this.displaySelectAnotherQuickPick(editor, false);652}653}654655return false;656}657658private async _calculdateKernelSources(editor: IActiveNotebookEditor) {659const notebook: NotebookTextModel = editor.textModel;660661const sourceActionCommands = this._notebookKernelService.getSourceActions(notebook, editor.scopedContextKeyService);662const actions = await this._notebookKernelService.getKernelSourceActions2(notebook);663const matchResult = this._getMatchingResult(notebook);664665if (sourceActionCommands.length === 0 && matchResult.all.length === 0 && actions.length === 0) {666return await this._getKernelRecommendationsQuickPickItems(notebook, this._extensionWorkbenchService) ?? [];667}668669const others = matchResult.all.filter(item => item.extension.value !== JUPYTER_EXTENSION_ID);670const quickPickItems: QuickPickInput<KernelQuickPickItem>[] = [];671672// group controllers by extension673for (const group of groupBy(others, (a, b) => a.extension.value === b.extension.value ? 0 : 1)) {674const extension = this._extensionService.extensions.find(extension => extension.identifier.value === group[0].extension.value);675const source = extension?.displayName ?? extension?.description ?? group[0].extension.value;676if (group.length > 1) {677quickPickItems.push({678label: source,679kernels: group680});681} else {682quickPickItems.push({683label: group[0].label,684kernel: group[0]685});686}687}688689const validActions = actions.filter(action => action.command);690691quickPickItems.push(...validActions.map(action => {692const buttons = action.documentation ? [{693iconClass: ThemeIcon.asClassName(Codicon.info),694tooltip: localize('learnMoreTooltip', 'Learn More'),695}] : [];696return {697id: typeof action.command! === 'string' ? action.command : action.command!.id,698label: action.label,699description: action.description,700command: action.command,701documentation: action.documentation,702buttons703};704}));705706for (const sourceAction of sourceActionCommands) {707const res: SourcePick = {708action: sourceAction,709picked: false,710label: sourceAction.action.label,711tooltip: sourceAction.action.tooltip712};713714quickPickItems.push(res);715}716717return quickPickItems;718}719720private async _selectOneKernel(notebook: NotebookTextModel, source: string, kernels: INotebookKernel[]) {721const quickPickItems: QuickPickInput<KernelPick>[] = kernels.map(kernel => toKernelQuickPick(kernel, undefined));722const localDisposableStore = new DisposableStore();723const quickPick = localDisposableStore.add(this._quickInputService.createQuickPick<KernelQuickPickItem>({ useSeparators: true }));724quickPick.items = quickPickItems;725quickPick.canSelectMany = false;726727quickPick.title = localize('selectKernelFromExtension', "Select Kernel from {0}", source);728729localDisposableStore.add(quickPick.onDidAccept(async () => {730if (quickPick.selectedItems && quickPick.selectedItems.length > 0 && isKernelPick(quickPick.selectedItems[0])) {731await this._selecteKernel(notebook, quickPick.selectedItems[0].kernel);732}733734quickPick.hide();735quickPick.dispose();736}));737738localDisposableStore.add(quickPick.onDidHide(() => {739localDisposableStore.dispose();740}));741742quickPick.show();743}744745private async _executeCommand<T>(notebook: NotebookTextModel, command: string | Command): Promise<T | undefined | void> {746const id = typeof command === 'string' ? command : command.id;747const args = typeof command === 'string' ? [] : command.arguments ?? [];748749if (typeof command === 'string' || !command.arguments || !Array.isArray(command.arguments) || command.arguments.length === 0) {750args.unshift({751uri: notebook.uri,752$mid: MarshalledId.NotebookActionContext753});754}755756if (typeof command === 'string') {757return this._commandService.executeCommand(id);758} else {759return this._commandService.executeCommand(id, ...args);760}761}762763static updateKernelStatusAction(notebook: NotebookTextModel, action: IAction, notebookKernelService: INotebookKernelService, notebookKernelHistoryService: INotebookKernelHistoryService) {764const detectionTasks = notebookKernelService.getKernelDetectionTasks(notebook);765if (detectionTasks.length) {766const info = notebookKernelService.getMatchingKernel(notebook);767action.enabled = true;768action.class = ThemeIcon.asClassName(ThemeIcon.modify(executingStateIcon, 'spin'));769770if (info.selected) {771action.label = info.selected.label;772const kernelInfo = info.selected.description ?? info.selected.detail;773action.tooltip = kernelInfo774? localize('kernels.selectedKernelAndKernelDetectionRunning', "Selected Kernel: {0} (Kernel Detection Tasks Running)", kernelInfo)775: localize('kernels.detecting', "Detecting Kernels");776} else {777action.label = localize('kernels.detecting', "Detecting Kernels");778}779return;780}781782const runningActions = notebookKernelService.getRunningSourceActions(notebook);783784const updateActionFromSourceAction = (sourceAction: ISourceAction, running: boolean) => {785const sAction = sourceAction.action;786action.class = running ? ThemeIcon.asClassName(ThemeIcon.modify(executingStateIcon, 'spin')) : ThemeIcon.asClassName(selectKernelIcon);787action.label = sAction.label;788action.enabled = true;789};790791if (runningActions.length) {792return updateActionFromSourceAction(runningActions[0] /** TODO handle multiple actions state */, true);793}794795const { selected } = notebookKernelHistoryService.getKernels(notebook);796797if (selected) {798action.label = selected.label;799action.class = ThemeIcon.asClassName(selectKernelIcon);800action.tooltip = selected.description ?? selected.detail ?? '';801} else {802action.label = localize('select', "Select Kernel");803action.class = ThemeIcon.asClassName(selectKernelIcon);804action.tooltip = '';805}806}807808static async resolveKernel(notebook: INotebookTextModel, notebookKernelService: INotebookKernelService, notebookKernelHistoryService: INotebookKernelHistoryService, commandService: ICommandService): Promise<INotebookKernel | undefined> {809const alreadySelected = notebookKernelHistoryService.getKernels(notebook);810811if (alreadySelected.selected) {812return alreadySelected.selected;813}814815await commandService.executeCommand(SELECT_KERNEL_ID);816const { selected } = notebookKernelHistoryService.getKernels(notebook);817return selected;818}819}820821822