Skip to content

Commit 685e763

Browse files
committed
Suggest install kernel extensions only when they are not installed.
1 parent aa37e17 commit 685e763

File tree

1 file changed

+77
-32
lines changed

1 file changed

+77
-32
lines changed

src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts

Lines changed: 77 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { groupBy } from 'vs/base/common/arrays';
7+
import { createCancelablePromise } from 'vs/base/common/async';
78
import { CancellationToken } from 'vs/base/common/cancellation';
89
import { Codicon } from 'vs/base/common/codicons';
910
import { Event } from 'vs/base/common/event';
@@ -20,7 +21,7 @@ import { ILabelService } from 'vs/platform/label/common/label';
2021
import { ILogService } from 'vs/platform/log/common/log';
2122
import { IProductService } from 'vs/platform/product/common/productService';
2223
import { ProgressLocation } from 'vs/platform/progress/common/progress';
23-
import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput';
24+
import { IQuickInputService, IQuickPick, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput';
2425
import { Registry } from 'vs/platform/registry/common/platform';
2526
import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
2627
import { ViewContainerLocation } from 'vs/workbench/common/views';
@@ -48,7 +49,11 @@ type SourcePick = IQuickPickItem & { action: ISourceAction };
4849
function isSourcePick(item: QuickPickInput<IQuickPickItem>): item is SourcePick {
4950
return 'action' in item;
5051
}
51-
type KernelQuickPickItem = IQuickPickItem | KernelPick | SourcePick;
52+
type InstallExtensionPick = IQuickPickItem & { extensionId: string };
53+
function isInstallExtensionPick(item: QuickPickInput<IQuickPickItem>): item is InstallExtensionPick {
54+
return item.id === 'installSuggested' && 'extensionId' in item;
55+
}
56+
type KernelQuickPickItem = IQuickPickItem | InstallExtensionPick | KernelPick | SourcePick;
5257
const KERNEL_PICKER_UPDATE_DEBOUNCE = 200;
5358

5459
registerAction2(class extends Action2 {
@@ -180,14 +185,18 @@ registerAction2(class extends Action2 {
180185
}
181186

182187
const quickPick = quickInputService.createQuickPick<KernelQuickPickItem>();
183-
const quickPickItemSuggestions = this._getKernelPickerQuickPickItems(notebook, matchResult, notebookKernelService, scopedContextKeyService);
184-
let suggestedExtension = quickPickItemSuggestions.suggestedExtension;
185-
quickPick.items = quickPickItemSuggestions.quickPickItems;
188+
const quickPickItems = this._getKernelPickerQuickPickItems(notebook, matchResult, notebookKernelService, scopedContextKeyService);
189+
quickPick.items = quickPickItems;
186190
quickPick.canSelectMany = false;
187191
quickPick.placeholder = selected
188192
? nls.localize('prompt.placeholder.change', "Change kernel for '{0}'", labelService.getUriLabel(notebook.uri, { relative: true }))
189193
: nls.localize('prompt.placeholder.select', "Select kernel for '{0}'", labelService.getUriLabel(notebook.uri, { relative: true }));
190194

195+
// run extension recommendataion task if quickPickItems is empty
196+
const extensionRecommendataionPromise = quickPickItems.length === 0
197+
? createCancelablePromise(token => this._showInstallKernelExtensionRecommendation(notebook, quickPick, extensionWorkbenchService, token))
198+
: undefined;
199+
191200
const kernelChangeEventListener = Event.debounce<void, void>(
192201
Event.any(
193202
notebookKernelService.onDidChangeSourceActions,
@@ -197,31 +206,34 @@ registerAction2(class extends Action2 {
197206
),
198207
(last, _current) => last,
199208
KERNEL_PICKER_UPDATE_DEBOUNCE
200-
)(() => {
209+
)(async () => {
210+
// reset quick pick progress
211+
quickPick.busy = false;
212+
extensionRecommendataionPromise?.cancel();
213+
201214
const currentActiveItems = quickPick.activeItems;
202215
const matchResult = notebookKernelService.getMatchingKernel(notebook);
203-
const quickPickItemSuggestions = this._getKernelPickerQuickPickItems(notebook, matchResult, notebookKernelService, scopedContextKeyService);
204-
suggestedExtension = quickPickItemSuggestions.suggestedExtension;
216+
const quickPickItems = this._getKernelPickerQuickPickItems(notebook, matchResult, notebookKernelService, scopedContextKeyService);
205217
quickPick.keepScrollPosition = true;
206218

207219
// recalcuate active items
208220
const activeItems: KernelQuickPickItem[] = [];
209221
for (const item of currentActiveItems) {
210222
if (isKernelPick(item)) {
211223
const kernelId = item.kernel.id;
212-
const sameItem = quickPickItemSuggestions.quickPickItems.find(pi => isKernelPick(pi) && pi.kernel.id === kernelId) as KernelPick | undefined;
224+
const sameItem = quickPickItems.find(pi => isKernelPick(pi) && pi.kernel.id === kernelId) as KernelPick | undefined;
213225
if (sameItem) {
214226
activeItems.push(sameItem);
215227
}
216228
} else if (isSourcePick(item)) {
217-
const sameItem = quickPickItemSuggestions.quickPickItems.find(pi => isSourcePick(pi) && pi.action.action.id === item.action.action.id) as SourcePick | undefined;
229+
const sameItem = quickPickItems.find(pi => isSourcePick(pi) && pi.action.action.id === item.action.action.id) as SourcePick | undefined;
218230
if (sameItem) {
219231
activeItems.push(sameItem);
220232
}
221233
}
222234
}
223235

224-
quickPick.items = quickPickItemSuggestions.quickPickItems;
236+
quickPick.items = quickPickItems;
225237
quickPick.activeItems = activeItems;
226238
}, this);
227239

@@ -261,13 +273,13 @@ registerAction2(class extends Action2 {
261273
notebook.viewType
262274
);
263275
// suggestedExtension must be defined for this option to be shown, but still check to make TS happy
264-
} else if (pick.id === 'installSuggested' && suggestedExtension) {
276+
} else if (isInstallExtensionPick(pick)) {
265277
await this._showKernelExtension(
266278
paneCompositeService,
267279
extensionWorkbenchService,
268280
extensionHostService,
269281
notebook.viewType,
270-
suggestedExtension.extensionId,
282+
pick.extensionId,
271283
productService.quality !== 'stable'
272284
);
273285
} else if (isSourcePick(pick)) {
@@ -284,7 +296,7 @@ registerAction2(class extends Action2 {
284296
matchResult: INotebookKernelMatchResult,
285297
notebookKernelService: INotebookKernelService,
286298
scopedContextKeyService: IContextKeyService
287-
): { quickPickItems: QuickPickInput<KernelQuickPickItem>[]; suggestedExtension: INotebookExtensionRecommendation | undefined } {
299+
): QuickPickInput<KernelQuickPickItem>[] {
288300
const { selected, all, suggestions, hidden } = matchResult;
289301

290302
function toQuickPick(kernel: INotebookKernel) {
@@ -346,29 +358,62 @@ registerAction2(class extends Action2 {
346358
});
347359
}
348360

349-
let suggestedExtension: INotebookExtensionRecommendation | undefined;
350-
if (!all.length && !sourceActions.length) {
351-
const language = this.getSuggestedLanguage(notebookTextModel);
352-
suggestedExtension = language ? this.getSuggestedKernelFromLanguage(notebookTextModel.viewType, language) : undefined;
353-
if (suggestedExtension) {
354-
// We have a suggested kernel, show an option to install it
355-
quickPickItems.push({
356-
id: 'installSuggested',
357-
description: suggestedExtension.displayName ?? suggestedExtension.extensionId,
358-
label: `$(${Codicon.lightbulb.id}) ` + nls.localize('installSuggestedKernel', 'Install suggested extensions'),
359-
});
361+
return quickPickItems;
362+
}
363+
364+
private async _showInstallKernelExtensionRecommendation(
365+
notebookTextModel: NotebookTextModel,
366+
quickPick: IQuickPick<KernelQuickPickItem>,
367+
extensionWorkbenchService: IExtensionsWorkbenchService,
368+
token: CancellationToken
369+
) {
370+
quickPick.busy = true;
371+
372+
const newQuickPickItems = await this._getKernelRecommendationsQuickPickItems(notebookTextModel, extensionWorkbenchService);
373+
quickPick.busy = false;
374+
375+
if (token.isCancellationRequested) {
376+
return;
377+
}
378+
379+
if (newQuickPickItems && quickPick.items.length === 0) {
380+
quickPick.items = newQuickPickItems;
381+
}
382+
}
383+
384+
385+
private async _getKernelRecommendationsQuickPickItems(
386+
notebookTextModel: NotebookTextModel,
387+
extensionWorkbenchService: IExtensionsWorkbenchService,
388+
): Promise<QuickPickInput<KernelQuickPickItem>[] | undefined> {
389+
const quickPickItems: QuickPickInput<KernelQuickPickItem>[] = [];
390+
391+
const language = this.getSuggestedLanguage(notebookTextModel);
392+
const suggestedExtension: INotebookExtensionRecommendation | undefined = language ? this.getSuggestedKernelFromLanguage(notebookTextModel.viewType, language) : undefined;
393+
if (suggestedExtension) {
394+
await extensionWorkbenchService.queryLocal();
395+
const extension = extensionWorkbenchService.installed.find(e => e.identifier.id === suggestedExtension.extensionId);
396+
397+
if (extension) {
398+
// it's installed but might be detecting kernels
399+
return undefined;
360400
}
361-
// there is no kernel, show the install from marketplace
401+
402+
// We have a suggested kernel, show an option to install it
362403
quickPickItems.push({
363-
id: 'install',
364-
label: nls.localize('searchForKernels', "Browse marketplace for kernel extensions"),
404+
id: 'installSuggested',
405+
description: suggestedExtension.displayName ?? suggestedExtension.extensionId,
406+
label: `$(${Codicon.lightbulb.id}) ` + nls.localize('installSuggestedKernel', 'Install suggested extensions'),
407+
extensionId: suggestedExtension.extensionId
365408
});
366409
}
410+
// there is no kernel, show the install from marketplace
411+
quickPickItems.push({
412+
id: 'install',
413+
label: nls.localize('searchForKernels', "Browse marketplace for kernel extensions"),
414+
});
367415

368-
return {
369-
quickPickItems,
370-
suggestedExtension
371-
};
416+
return quickPickItems;
372417
}
373418

374419
/**

0 commit comments

Comments
 (0)