Skip to content

Commit ea3a260

Browse files
authored
kernel mru history per workspace (microsoft#166633)
1 parent 8f2c8e3 commit ea3a260

File tree

5 files changed

+363
-11
lines changed

5 files changed

+363
-11
lines changed

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
4646
import { Event } from 'vs/base/common/event';
4747
import { getFormattedMetadataJSON, getStreamOutputData } from 'vs/workbench/contrib/notebook/browser/diff/diffElementViewModel';
4848
import { NotebookModelResolverServiceImpl } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverServiceImpl';
49-
import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService';
49+
import { INotebookKernelHistoryService, INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService';
5050
import { NotebookKernelService } from 'vs/workbench/contrib/notebook/browser/services/notebookKernelServiceImpl';
5151
import { IWorkingCopyIdentifier } from 'vs/workbench/services/workingCopy/common/workingCopy';
5252
import { IResourceEditorInput } from 'vs/platform/editor/common/editor';
@@ -105,6 +105,7 @@ import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeat
105105
import { NotebookInfo } from 'vs/editor/common/languageFeatureRegistry';
106106
import { COMMENTEDITOR_DECORATION_KEY } from 'vs/workbench/contrib/comments/browser/commentReply';
107107
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
108+
import { NotebookKernelHistoryService } from 'vs/workbench/contrib/notebook/browser/services/notebookKernelHistoryServiceImpl';
108109

109110
/*--------------------------------------------------------------------------------------------- */
110111

@@ -703,6 +704,7 @@ registerSingleton(INotebookEditorModelResolverService, NotebookModelResolverServ
703704
registerSingleton(INotebookCellStatusBarService, NotebookCellStatusBarService, InstantiationType.Delayed);
704705
registerSingleton(INotebookEditorService, NotebookEditorWidgetService, InstantiationType.Delayed);
705706
registerSingleton(INotebookKernelService, NotebookKernelService, InstantiationType.Delayed);
707+
registerSingleton(INotebookKernelHistoryService, NotebookKernelHistoryService, InstantiationType.Delayed);
706708
registerSingleton(INotebookExecutionService, NotebookExecutionService, InstantiationType.Delayed);
707709
registerSingleton(INotebookExecutionStateService, NotebookExecutionStateService, InstantiationType.Delayed);
708710
registerSingleton(INotebookRendererMessagingService, NotebookRendererMessagingService, InstantiationType.Delayed);
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { Disposable } from 'vs/base/common/lifecycle';
7+
import { LinkedMap, Touch } from 'vs/base/common/map';
8+
import { localize } from 'vs/nls';
9+
import { Categories } from 'vs/platform/action/common/actionCommonCategories';
10+
import { Action2, registerAction2 } from 'vs/platform/actions/common/actions';
11+
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
12+
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
13+
import { INotebookKernel, INotebookKernelHistoryService, INotebookKernelService, INotebookTextModelLike } from 'vs/workbench/contrib/notebook/common/notebookKernelService';
14+
15+
interface ISerializedKernelsListPerType {
16+
entries: string[];
17+
}
18+
19+
interface ISerializedKernelsList {
20+
[viewType: string]: ISerializedKernelsListPerType;
21+
}
22+
23+
const MAX_KERNELS_IN_HISTORY = 5;
24+
25+
export class NotebookKernelHistoryService extends Disposable implements INotebookKernelHistoryService {
26+
declare _serviceBrand: undefined;
27+
28+
private static STORAGE_KEY = 'notebook.kernelHistory';
29+
private _mostRecentKernelsMap: { [key: string]: LinkedMap<string, string> } = {};
30+
31+
constructor(@IStorageService private readonly _storageService: IStorageService,
32+
@INotebookKernelService private readonly _notebookKernelService: INotebookKernelService) {
33+
super();
34+
35+
this._loadState();
36+
this._register(this._storageService.onWillSaveState(() => this._saveState()));
37+
}
38+
39+
getKernels(notebook: INotebookTextModelLike): { selected: INotebookKernel | undefined; all: INotebookKernel[] } {
40+
const allAvailableKernels = this._notebookKernelService.getMatchingKernel(notebook);
41+
const allKernels = allAvailableKernels.all;
42+
const selectedKernel = allAvailableKernels.selected;
43+
const suggested = (allAvailableKernels.suggestions.length === 1 ? allAvailableKernels.suggestions[0] : undefined)
44+
?? (allAvailableKernels.all.length === 1) ? allAvailableKernels.all[0] : undefined;
45+
46+
const mostRecentKernelIds = this._mostRecentKernelsMap[notebook.viewType] ? [...this._mostRecentKernelsMap[notebook.viewType].values()] : [];
47+
48+
const all = mostRecentKernelIds.map(kernelId => allKernels.find(kernel => kernel.id === kernelId)).filter(kernel => !!kernel) as INotebookKernel[];
49+
50+
return {
51+
selected: selectedKernel ?? suggested,
52+
all
53+
};
54+
}
55+
56+
addMostRecentKernel(kernel: INotebookKernel): void {
57+
const key = kernel.id;
58+
const viewType = kernel.viewType;
59+
const recentKeynels = this._mostRecentKernelsMap[viewType] ?? new LinkedMap<string, string>();
60+
61+
recentKeynels.set(key, key, Touch.AsOld);
62+
63+
64+
if (recentKeynels.size > MAX_KERNELS_IN_HISTORY) {
65+
const reserved = [...recentKeynels.entries()].slice(0, MAX_KERNELS_IN_HISTORY);
66+
recentKeynels.fromJSON(reserved);
67+
}
68+
69+
this._mostRecentKernelsMap[viewType] = recentKeynels;
70+
}
71+
72+
private _saveState(): void {
73+
let notEmpty = false;
74+
for (const [_, kernels] of Object.entries(this._mostRecentKernelsMap)) {
75+
notEmpty = notEmpty || kernels.size > 0;
76+
}
77+
78+
if (notEmpty) {
79+
const serialized = this._serialize();
80+
this._storageService.store(NotebookKernelHistoryService.STORAGE_KEY, JSON.stringify(serialized), StorageScope.WORKSPACE, StorageTarget.MACHINE);
81+
} else {
82+
this._storageService.remove(NotebookKernelHistoryService.STORAGE_KEY, StorageScope.WORKSPACE);
83+
}
84+
}
85+
86+
private _loadState(): void {
87+
const serialized = this._storageService.get(NotebookKernelHistoryService.STORAGE_KEY, StorageScope.WORKSPACE);
88+
if (serialized) {
89+
try {
90+
this._deserialize(JSON.parse(serialized));
91+
} catch (e) {
92+
this._mostRecentKernelsMap = {};
93+
}
94+
} else {
95+
this._mostRecentKernelsMap = {};
96+
}
97+
}
98+
99+
private _serialize(): ISerializedKernelsList {
100+
const result: ISerializedKernelsList = Object.create(null);
101+
102+
for (const [viewType, kernels] of Object.entries(this._mostRecentKernelsMap)) {
103+
result[viewType] = {
104+
entries: [...kernels.values()]
105+
};
106+
}
107+
return result;
108+
}
109+
110+
private _deserialize(serialized: ISerializedKernelsList): void {
111+
this._mostRecentKernelsMap = {};
112+
113+
for (const [viewType, kernels] of Object.entries(serialized)) {
114+
const linkedMap = new LinkedMap<string, string>();
115+
const mapValues: [string, string][] = [];
116+
117+
for (const entry of kernels.entries) {
118+
mapValues.push([entry, entry]);
119+
}
120+
121+
linkedMap.fromJSON(mapValues);
122+
this._mostRecentKernelsMap[viewType] = linkedMap;
123+
}
124+
}
125+
126+
_clear(): void {
127+
this._mostRecentKernelsMap = {};
128+
this._saveState();
129+
}
130+
}
131+
132+
registerAction2(class extends Action2 {
133+
constructor() {
134+
super({
135+
id: 'notebook.clearNotebookKernelsMRUCache',
136+
title: {
137+
value: localize('workbench.notebook.clearNotebookKernelsMRUCache', "Clear Notebook Kernels MRU Cache"),
138+
original: 'Clear Notebook Kernels MRU Cache'
139+
},
140+
category: Categories.Developer,
141+
f1: true
142+
});
143+
}
144+
145+
async run(accessor: ServicesAccessor): Promise<void> {
146+
const historyService = accessor.get(INotebookKernelHistoryService) as NotebookKernelHistoryService;
147+
historyService._clear();
148+
}
149+
});

src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelQuickPickStrategy.ts

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { IExtensionsViewPaneContainer, IExtensionsWorkbenchService, VIEWLET_ID a
2323
import { getNotebookEditorFromEditorPane, INotebookEditor, INotebookExtensionRecommendation, JUPYTER_EXTENSION_ID, KERNEL_RECOMMENDATIONS } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
2424
import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget';
2525
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
26-
import { INotebookKernel, INotebookKernelMatchResult, INotebookKernelService, ISourceAction } from 'vs/workbench/contrib/notebook/common/notebookKernelService';
26+
import { INotebookKernel, INotebookKernelHistoryService, INotebookKernelMatchResult, INotebookKernelService, ISourceAction } from 'vs/workbench/contrib/notebook/common/notebookKernelService';
2727
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
2828
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
2929
import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite';
@@ -129,7 +129,7 @@ abstract class KernelPickerStrategyBase implements IKernelPickerStrategy {
129129

130130
const notebook = editor.textModel;
131131
const scopedContextKeyService = editor.scopedContextKeyService;
132-
const matchResult = this._notebookKernelService.getMatchingKernel(notebook);
132+
const matchResult = this._getMatchingResult(notebook);
133133
const { selected, all } = matchResult;
134134

135135
if (selected && controllerId && selected.id === controllerId && ExtensionIdentifier.equals(selected.extension, extensionId)) {
@@ -153,7 +153,7 @@ abstract class KernelPickerStrategyBase implements IKernelPickerStrategy {
153153
}
154154

155155
if (newKernel) {
156-
this._notebookKernelService.selectKernelForNotebook(newKernel, notebook);
156+
this._selecteKernel(notebook, newKernel);
157157
return true;
158158
}
159159

@@ -191,7 +191,7 @@ abstract class KernelPickerStrategyBase implements IKernelPickerStrategy {
191191
extensionRecommendataionPromise?.cancel();
192192

193193
const currentActiveItems = quickPick.activeItems;
194-
const matchResult = this._notebookKernelService.getMatchingKernel(notebook);
194+
const matchResult = this._getMatchingResult(notebook);
195195
const quickPickItems = this._getKernelPickerQuickPickItems(notebook, matchResult, this._notebookKernelService, scopedContextKeyService);
196196
quickPick.keepScrollPosition = true;
197197

@@ -244,6 +244,10 @@ abstract class KernelPickerStrategyBase implements IKernelPickerStrategy {
244244
return false;
245245
}
246246

247+
protected _getMatchingResult(notebook: NotebookTextModel) {
248+
return this._notebookKernelService.getMatchingKernel(notebook);
249+
}
250+
247251
protected abstract _getKernelPickerQuickPickItems(
248252
notebookTextModel: NotebookTextModel,
249253
matchResult: INotebookKernelMatchResult,
@@ -254,7 +258,7 @@ abstract class KernelPickerStrategyBase implements IKernelPickerStrategy {
254258
protected async _handleQuickPick(notebook: NotebookTextModel, pick: KernelQuickPickItem, context?: KernelQuickPickContext) {
255259
if (isKernelPick(pick)) {
256260
const newKernel = pick.kernel;
257-
this._notebookKernelService.selectKernelForNotebook(newKernel, notebook);
261+
this._selecteKernel(notebook, newKernel);
258262
return true;
259263
}
260264

@@ -284,6 +288,10 @@ abstract class KernelPickerStrategyBase implements IKernelPickerStrategy {
284288
return true;
285289
}
286290

291+
protected _selecteKernel(notebook: NotebookTextModel, kernel: INotebookKernel) {
292+
this._notebookKernelService.selectKernelForNotebook(kernel, notebook);
293+
}
294+
287295
private async _showKernelExtension(
288296
paneCompositePartService: IPaneCompositePartService,
289297
extensionWorkbenchService: IExtensionsWorkbenchService,
@@ -574,7 +582,8 @@ export class KernelPickerMRUStrategy extends KernelPickerStrategyBase {
574582
@IPaneCompositePartService _paneCompositePartService: IPaneCompositePartService,
575583
@IExtensionsWorkbenchService _extensionWorkbenchService: IExtensionsWorkbenchService,
576584
@IExtensionService _extensionService: IExtensionService,
577-
@ICommandService _commandService: ICommandService
585+
@ICommandService _commandService: ICommandService,
586+
@INotebookKernelHistoryService private readonly _notebookKernelHistoryService: INotebookKernelHistoryService,
578587

579588
) {
580589
super(
@@ -590,6 +599,7 @@ export class KernelPickerMRUStrategy extends KernelPickerStrategyBase {
590599
_commandService,
591600
);
592601
}
602+
593603
protected _getKernelPickerQuickPickItems(notebookTextModel: NotebookTextModel, matchResult: INotebookKernelMatchResult, notebookKernelService: INotebookKernelService, scopedContextKeyService: IContextKeyService): QuickPickInput<KernelQuickPickItem>[] {
594604
const quickPickItems: QuickPickInput<KernelQuickPickItem>[] = [];
595605
let previousKind = '';
@@ -627,6 +637,22 @@ export class KernelPickerMRUStrategy extends KernelPickerStrategyBase {
627637
return quickPickItems;
628638
}
629639

640+
protected override _selecteKernel(notebook: NotebookTextModel, kernel: INotebookKernel): void {
641+
super._selecteKernel(notebook, kernel);
642+
this._notebookKernelHistoryService.addMostRecentKernel(kernel);
643+
}
644+
645+
protected override _getMatchingResult(notebook: NotebookTextModel): INotebookKernelMatchResult {
646+
const { selected, all } = this._notebookKernelHistoryService.getKernels(notebook);
647+
const matchingResult = this._notebookKernelService.getMatchingKernel(notebook);
648+
return {
649+
selected: selected,
650+
all: matchingResult.all,
651+
suggestions: all,
652+
hidden: []
653+
};
654+
}
655+
630656
protected override async _handleQuickPick(notebook: NotebookTextModel, pick: KernelQuickPickItem, context?: KernelQuickPickContext): Promise<boolean> {
631657
if (pick.id === 'selectAnother') {
632658
return this.displaySelectAnotherQuickPick(notebook, context);
@@ -659,25 +685,25 @@ export class KernelPickerMRUStrategy extends KernelPickerStrategyBase {
659685
if ('command' in quickPick.selectedItems[0]) {
660686
const selectedKernelId = await this._executeCommand<string>(notebook, quickPick.selectedItems[0].command);
661687
if (selectedKernelId) {
662-
const { all } = await this._notebookKernelService.getMatchingKernel(notebook);
688+
const { all } = await this._getMatchingResult(notebook);
663689
const kernel = all.find(kernel => kernel.id === `ms-toolsai.jupyter/${selectedKernelId}`);
664690
if (kernel) {
665-
await this._notebookKernelService.selectKernelForNotebook(kernel, notebook);
691+
await this._selecteKernel(notebook, kernel);
666692
resolve(true);
667693
}
668694
resolve(true);
669695
} else {
670696
return resolve(this.displaySelectAnotherQuickPick(notebook));
671697
}
672698
} else if ('kernel' in quickPick.selectedItems[0]) {
673-
await this._notebookKernelService.selectKernelForNotebook(quickPick.selectedItems[0].kernel, notebook);
699+
await this._selecteKernel(notebook, quickPick.selectedItems[0].kernel);
674700
resolve(true);
675701
}
676702
}
677703
}));
678704
this._notebookKernelService.getKernelSourceActions2(notebook).then(actions => {
679705
quickPick.busy = false;
680-
const matchResult = this._notebookKernelService.getMatchingKernel(notebook);
706+
const matchResult = this._getMatchingResult(notebook);
681707
const others = matchResult.all.filter(item => item.extension.value !== JUPYTER_EXTENSION_ID);
682708
quickPickItems.push(...others.map(kernel => ({
683709
label: kernel.label,

src/vs/workbench/contrib/notebook/common/notebookKernelService.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,10 @@ export interface INotebookKernelService {
139139
getKernelSourceActions2(notebook: INotebookTextModelLike): Promise<INotebookKernelSourceAction[]>;
140140
//#endregion
141141
}
142+
143+
export const INotebookKernelHistoryService = createDecorator<INotebookKernelHistoryService>('INotebookKernelHistoryService');
144+
export interface INotebookKernelHistoryService {
145+
_serviceBrand: undefined;
146+
getKernels(notebook: INotebookTextModelLike): { selected: INotebookKernel | undefined; all: INotebookKernel[] };
147+
addMostRecentKernel(kernel: INotebookKernel): void;
148+
}

0 commit comments

Comments
 (0)