Path: blob/main/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelQuickPickStrategy.ts
3296 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;423let suggestedKernelLanguage: string | undefined = (metaData as any)?.metadata?.language_info?.name;424// TODO how do we suggest multi language notebooks?425if (!suggestedKernelLanguage) {426const cellLanguages = notebookTextModel.cells.map(cell => cell.language).filter(language => language !== 'markdown');427// Check if cell languages is all the same428if (cellLanguages.length > 1) {429const firstLanguage = cellLanguages[0];430if (cellLanguages.every(language => language === firstLanguage)) {431suggestedKernelLanguage = firstLanguage;432}433}434}435return suggestedKernelLanguage;436}437438/**439* Given a language and notebook view type suggest a kernel for installation440* @param language The language to find a suggested kernel extension for441* @returns A recommednation object for the recommended extension, else undefined442*/443private getSuggestedKernelFromLanguage(viewType: string, language: string): INotebookExtensionRecommendation | undefined {444const recommendation = KERNEL_RECOMMENDATIONS.get(viewType)?.get(language);445return recommendation;446}447}448449export class KernelPickerMRUStrategy extends KernelPickerStrategyBase {450constructor(451@INotebookKernelService _notebookKernelService: INotebookKernelService,452@IProductService _productService: IProductService,453@IQuickInputService _quickInputService: IQuickInputService,454@ILabelService _labelService: ILabelService,455@ILogService _logService: ILogService,456@IExtensionsWorkbenchService _extensionWorkbenchService: IExtensionsWorkbenchService,457@IExtensionService _extensionService: IExtensionService,458@IExtensionManagementServerService _extensionManagementServerService: IExtensionManagementServerService,459@ICommandService _commandService: ICommandService,460@INotebookKernelHistoryService private readonly _notebookKernelHistoryService: INotebookKernelHistoryService,461@IOpenerService private readonly _openerService: IOpenerService462463) {464super(465_notebookKernelService,466_productService,467_quickInputService,468_labelService,469_logService,470_extensionWorkbenchService,471_extensionService,472_commandService,473_extensionManagementServerService,474);475}476477protected _getKernelPickerQuickPickItems(notebookTextModel: NotebookTextModel, matchResult: INotebookKernelMatchResult, notebookKernelService: INotebookKernelService, scopedContextKeyService: IContextKeyService): QuickPickInput<KernelQuickPickItem>[] {478const quickPickItems: QuickPickInput<KernelQuickPickItem>[] = [];479480if (matchResult.selected) {481const kernelItem = toKernelQuickPick(matchResult.selected, matchResult.selected);482quickPickItems.push(kernelItem);483}484485matchResult.suggestions.filter(kernel => kernel.id !== matchResult.selected?.id).map(kernel => toKernelQuickPick(kernel, matchResult.selected))486.forEach(kernel => {487quickPickItems.push(kernel);488});489490const shouldAutoRun = quickPickItems.length === 0;491492if (quickPickItems.length > 0) {493quickPickItems.push({494type: 'separator'495});496}497498// select another kernel quick pick499quickPickItems.push({500id: 'selectAnother',501label: localize('selectAnotherKernel.more', "Select Another Kernel..."),502autoRun: shouldAutoRun503});504505return quickPickItems;506}507508protected override _selecteKernel(notebook: NotebookTextModel, kernel: INotebookKernel): void {509const currentInfo = this._notebookKernelService.getMatchingKernel(notebook);510if (currentInfo.selected) {511// there is already a selected kernel512this._notebookKernelHistoryService.addMostRecentKernel(currentInfo.selected);513}514super._selecteKernel(notebook, kernel);515this._notebookKernelHistoryService.addMostRecentKernel(kernel);516}517518protected override _getMatchingResult(notebook: NotebookTextModel): INotebookKernelMatchResult {519const { selected, all } = this._notebookKernelHistoryService.getKernels(notebook);520const matchingResult = this._notebookKernelService.getMatchingKernel(notebook);521return {522selected: selected,523all: matchingResult.all,524suggestions: all,525hidden: []526};527}528529protected override async _handleQuickPick(editor: IActiveNotebookEditor, pick: KernelQuickPickItem, items: KernelQuickPickItem[]): Promise<boolean> {530if (pick.id === 'selectAnother') {531return this.displaySelectAnotherQuickPick(editor, items.length === 1 && items[0] === pick);532}533534return super._handleQuickPick(editor, pick, items);535}536537private async displaySelectAnotherQuickPick(editor: IActiveNotebookEditor, kernelListEmpty: boolean): Promise<boolean> {538const notebook: NotebookTextModel = editor.textModel;539const disposables = new DisposableStore();540const quickPick = disposables.add(this._quickInputService.createQuickPick<KernelQuickPickItem>({ useSeparators: true }));541const quickPickItem = await new Promise<KernelQuickPickItem | IQuickInputButton | undefined>(resolve => {542// select from kernel sources543quickPick.title = kernelListEmpty ? localize('select', "Select Kernel") : localize('selectAnotherKernel', "Select Another Kernel");544quickPick.placeholder = localize('selectKernel.placeholder', "Type to choose a kernel source");545quickPick.busy = true;546quickPick.buttons = [this._quickInputService.backButton];547quickPick.show();548549disposables.add(quickPick.onDidTriggerButton(button => {550if (button === this._quickInputService.backButton) {551resolve(button);552}553}));554disposables.add(quickPick.onDidTriggerItemButton(async (e) => {555if (isKernelSourceQuickPickItem(e.item) && e.item.documentation !== undefined) {556const uri = URI.isUri(e.item.documentation) ? URI.parse(e.item.documentation) : await this._commandService.executeCommand(e.item.documentation);557void this._openerService.open(uri, { openExternal: true });558}559}));560disposables.add(quickPick.onDidAccept(async () => {561resolve(quickPick.selectedItems[0]);562}));563disposables.add(quickPick.onDidHide(() => {564resolve(undefined);565}));566567this._calculdateKernelSources(editor).then(quickPickItems => {568quickPick.items = quickPickItems;569if (quickPick.items.length > 0) {570quickPick.busy = false;571}572});573574disposables.add(Event.debounce<void, void>(575Event.any(576this._notebookKernelService.onDidChangeSourceActions,577this._notebookKernelService.onDidAddKernel,578this._notebookKernelService.onDidRemoveKernel579),580(last, _current) => last,581KERNEL_PICKER_UPDATE_DEBOUNCE582)(async () => {583quickPick.busy = true;584const quickPickItems = await this._calculdateKernelSources(editor);585quickPick.items = quickPickItems;586quickPick.busy = false;587}));588});589590quickPick.hide();591disposables.dispose();592593if (quickPickItem === this._quickInputService.backButton) {594return this.showQuickPick(editor, undefined, true);595}596597if (quickPickItem) {598const selectedKernelPickItem = quickPickItem as KernelQuickPickItem;599if (isKernelSourceQuickPickItem(selectedKernelPickItem)) {600try {601const selectedKernelId = await this._executeCommand<string>(notebook, selectedKernelPickItem.command);602if (selectedKernelId) {603const { all } = await this._getMatchingResult(notebook);604const kernel = all.find(kernel => kernel.id === `ms-toolsai.jupyter/${selectedKernelId}`);605if (kernel) {606await this._selecteKernel(notebook, kernel);607return true;608}609return true;610} else {611return this.displaySelectAnotherQuickPick(editor, false);612}613} catch (ex) {614return false;615}616} else if (isKernelPick(selectedKernelPickItem)) {617await this._selecteKernel(notebook, selectedKernelPickItem.kernel);618return true;619} else if (isGroupedKernelsPick(selectedKernelPickItem)) {620await this._selectOneKernel(notebook, selectedKernelPickItem.label, selectedKernelPickItem.kernels);621return true;622} else if (isSourcePick(selectedKernelPickItem)) {623// selected explicilty, it should trigger the execution?624try {625await selectedKernelPickItem.action.runAction();626return true;627} catch (ex) {628return false;629}630} else if (isSearchMarketplacePick(selectedKernelPickItem)) {631await this._showKernelExtension(632this._extensionWorkbenchService,633this._extensionService,634this._extensionManagementServerService,635editor.textModel.viewType,636[]637);638return true;639} else if (isInstallExtensionPick(selectedKernelPickItem)) {640await this._showKernelExtension(641this._extensionWorkbenchService,642this._extensionService,643this._extensionManagementServerService,644editor.textModel.viewType,645selectedKernelPickItem.extensionIds,646this._productService.quality !== 'stable'647);648return this.displaySelectAnotherQuickPick(editor, false);649}650}651652return false;653}654655private async _calculdateKernelSources(editor: IActiveNotebookEditor) {656const notebook: NotebookTextModel = editor.textModel;657658const sourceActionCommands = this._notebookKernelService.getSourceActions(notebook, editor.scopedContextKeyService);659const actions = await this._notebookKernelService.getKernelSourceActions2(notebook);660const matchResult = this._getMatchingResult(notebook);661662if (sourceActionCommands.length === 0 && matchResult.all.length === 0 && actions.length === 0) {663return await this._getKernelRecommendationsQuickPickItems(notebook, this._extensionWorkbenchService) ?? [];664}665666const others = matchResult.all.filter(item => item.extension.value !== JUPYTER_EXTENSION_ID);667const quickPickItems: QuickPickInput<KernelQuickPickItem>[] = [];668669// group controllers by extension670for (const group of groupBy(others, (a, b) => a.extension.value === b.extension.value ? 0 : 1)) {671const extension = this._extensionService.extensions.find(extension => extension.identifier.value === group[0].extension.value);672const source = extension?.displayName ?? extension?.description ?? group[0].extension.value;673if (group.length > 1) {674quickPickItems.push({675label: source,676kernels: group677});678} else {679quickPickItems.push({680label: group[0].label,681kernel: group[0]682});683}684}685686const validActions = actions.filter(action => action.command);687688quickPickItems.push(...validActions.map(action => {689const buttons = action.documentation ? [{690iconClass: ThemeIcon.asClassName(Codicon.info),691tooltip: localize('learnMoreTooltip', 'Learn More'),692}] : [];693return {694id: typeof action.command! === 'string' ? action.command : action.command!.id,695label: action.label,696description: action.description,697command: action.command,698documentation: action.documentation,699buttons700};701}));702703for (const sourceAction of sourceActionCommands) {704const res: SourcePick = {705action: sourceAction,706picked: false,707label: sourceAction.action.label,708tooltip: sourceAction.action.tooltip709};710711quickPickItems.push(res);712}713714return quickPickItems;715}716717private async _selectOneKernel(notebook: NotebookTextModel, source: string, kernels: INotebookKernel[]) {718const quickPickItems: QuickPickInput<KernelPick>[] = kernels.map(kernel => toKernelQuickPick(kernel, undefined));719const localDisposableStore = new DisposableStore();720const quickPick = localDisposableStore.add(this._quickInputService.createQuickPick<KernelQuickPickItem>({ useSeparators: true }));721quickPick.items = quickPickItems;722quickPick.canSelectMany = false;723724quickPick.title = localize('selectKernelFromExtension', "Select Kernel from {0}", source);725726localDisposableStore.add(quickPick.onDidAccept(async () => {727if (quickPick.selectedItems && quickPick.selectedItems.length > 0 && isKernelPick(quickPick.selectedItems[0])) {728await this._selecteKernel(notebook, quickPick.selectedItems[0].kernel);729}730731quickPick.hide();732quickPick.dispose();733}));734735localDisposableStore.add(quickPick.onDidHide(() => {736localDisposableStore.dispose();737}));738739quickPick.show();740}741742private async _executeCommand<T>(notebook: NotebookTextModel, command: string | Command): Promise<T | undefined | void> {743const id = typeof command === 'string' ? command : command.id;744const args = typeof command === 'string' ? [] : command.arguments ?? [];745746if (typeof command === 'string' || !command.arguments || !Array.isArray(command.arguments) || command.arguments.length === 0) {747args.unshift({748uri: notebook.uri,749$mid: MarshalledId.NotebookActionContext750});751}752753if (typeof command === 'string') {754return this._commandService.executeCommand(id);755} else {756return this._commandService.executeCommand(id, ...args);757}758}759760static updateKernelStatusAction(notebook: NotebookTextModel, action: IAction, notebookKernelService: INotebookKernelService, notebookKernelHistoryService: INotebookKernelHistoryService) {761const detectionTasks = notebookKernelService.getKernelDetectionTasks(notebook);762if (detectionTasks.length) {763const info = notebookKernelService.getMatchingKernel(notebook);764action.enabled = true;765action.class = ThemeIcon.asClassName(ThemeIcon.modify(executingStateIcon, 'spin'));766767if (info.selected) {768action.label = info.selected.label;769const kernelInfo = info.selected.description ?? info.selected.detail;770action.tooltip = kernelInfo771? localize('kernels.selectedKernelAndKernelDetectionRunning', "Selected Kernel: {0} (Kernel Detection Tasks Running)", kernelInfo)772: localize('kernels.detecting', "Detecting Kernels");773} else {774action.label = localize('kernels.detecting', "Detecting Kernels");775}776return;777}778779const runningActions = notebookKernelService.getRunningSourceActions(notebook);780781const updateActionFromSourceAction = (sourceAction: ISourceAction, running: boolean) => {782const sAction = sourceAction.action;783action.class = running ? ThemeIcon.asClassName(ThemeIcon.modify(executingStateIcon, 'spin')) : ThemeIcon.asClassName(selectKernelIcon);784action.label = sAction.label;785action.enabled = true;786};787788if (runningActions.length) {789return updateActionFromSourceAction(runningActions[0] /** TODO handle multiple actions state */, true);790}791792const { selected } = notebookKernelHistoryService.getKernels(notebook);793794if (selected) {795action.label = selected.label;796action.class = ThemeIcon.asClassName(selectKernelIcon);797action.tooltip = selected.description ?? selected.detail ?? '';798} else {799action.label = localize('select', "Select Kernel");800action.class = ThemeIcon.asClassName(selectKernelIcon);801action.tooltip = '';802}803}804805static async resolveKernel(notebook: INotebookTextModel, notebookKernelService: INotebookKernelService, notebookKernelHistoryService: INotebookKernelHistoryService, commandService: ICommandService): Promise<INotebookKernel | undefined> {806const alreadySelected = notebookKernelHistoryService.getKernels(notebook);807808if (alreadySelected.selected) {809return alreadySelected.selected;810}811812await commandService.executeCommand(SELECT_KERNEL_ID);813const { selected } = notebookKernelHistoryService.getKernels(notebook);814return selected;815}816}817818819