Skip to content

Commit 292eaef

Browse files
committed
refactor the manager and serializer.
1 parent 847b66b commit 292eaef

14 files changed

+162
-115
lines changed

package.json

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,6 @@
5454
"Visualization"
5555
],
5656
"activationEvents": [
57-
"onCommand:deepnote.openFile",
58-
"onCommand:deepnote.openNotebook",
59-
"onCommand:deepnote.refreshExplorer",
60-
"onCommand:deepnote.revealInExplorer",
6157
"onLanguage:jupyter",
6258
"onLanguage:python",
6359
"onLanguageModelTool:configure_notebook",
@@ -261,18 +257,6 @@
261257
"category": "Deepnote",
262258
"icon": "$(refresh)"
263259
},
264-
{
265-
"command": "deepnote.openNotebook",
266-
"title": "%deepnote.commands.openNotebook.title%",
267-
"category": "Deepnote",
268-
"icon": "$(notebook)"
269-
},
270-
{
271-
"command": "deepnote.openFile",
272-
"title": "%deepnote.commands.openFile.title%",
273-
"category": "Deepnote",
274-
"icon": "$(go-to-file)"
275-
},
276260
{
277261
"command": "deepnote.revealInExplorer",
278262
"title": "%deepnote.commands.revealInExplorer.title%",
@@ -348,12 +332,6 @@
348332
"title": "%jupyter.command.jupyter.viewOutput.title%",
349333
"category": "Jupyter"
350334
},
351-
{
352-
"command": "jupyter.selectDeepnoteNotebook",
353-
"title": "%deepnote.command.selectNotebook.title%",
354-
"category": "Deepnote",
355-
"enablement": "notebookType == 'deepnote'"
356-
},
357335
{
358336
"command": "jupyter.notebookeditor.export",
359337
"title": "%DataScience.notebookExportAs%",
@@ -922,11 +900,6 @@
922900
"group": "navigation@2",
923901
"when": "notebookType == 'jupyter-notebook' && config.jupyter.showOutlineButtonInNotebookToolbar"
924902
},
925-
{
926-
"command": "jupyter.selectDeepnoteNotebook",
927-
"group": "navigation@2",
928-
"when": "notebookType == 'deepnote'"
929-
},
930903
{
931904
"command": "jupyter.continueEditSessionInCodespace",
932905
"group": "navigation@3",
@@ -1432,16 +1405,6 @@
14321405
}
14331406
],
14341407
"view/item/context": [
1435-
{
1436-
"command": "deepnote.openNotebook",
1437-
"when": "view == deepnoteExplorer",
1438-
"group": "inline@0"
1439-
},
1440-
{
1441-
"command": "deepnote.openFile",
1442-
"when": "view == deepnoteExplorer",
1443-
"group": "inline@1"
1444-
},
14451408
{
14461409
"command": "deepnote.revealInExplorer",
14471410
"when": "view == deepnoteExplorer",
@@ -2052,7 +2015,7 @@
20522015
{
20532016
"id": "deepnote",
20542017
"title": "Deepnote",
2055-
"icon": "resources/dark/deepnote-icon.svg"
2018+
"icon": "resources/DnDeepnoteLineLogo.svg"
20562019
}
20572020
],
20582021
"panel": [

package.nls.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,5 +249,6 @@
249249
"deepnote.commands.openNotebook.title": "Open Notebook",
250250
"deepnote.commands.openFile.title": "Open File",
251251
"deepnote.commands.revealInExplorer.title": "Reveal in Explorer",
252-
"deepnote.views.explorer.name": "Explorer"
252+
"deepnote.views.explorer.name": "Explorer",
253+
"deepnote.command.selectNotebook.title": "Select Notebook"
253254
}

resources/DnDeepnoteLineLogo.svg

Lines changed: 3 additions & 0 deletions
Loading

src/notebooks/deepnote/deepnoteActivationService.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { injectable, inject } from 'inversify';
22
import { workspace } from 'vscode';
33
import { IExtensionSyncActivationService } from '../../platform/activation/types';
44
import { IExtensionContext } from '../../platform/common/types';
5+
import { IDeepnoteNotebookManager } from '../types';
56
import { DeepnoteNotebookSerializer } from './deepnoteSerializer';
67
import { DeepnoteExplorerView } from './deepnoteExplorerView';
78

@@ -14,15 +15,18 @@ export class DeepnoteActivationService implements IExtensionSyncActivationServic
1415
private explorerView: DeepnoteExplorerView;
1516
private serializer: DeepnoteNotebookSerializer;
1617

17-
constructor(@inject(IExtensionContext) private extensionContext: IExtensionContext) {}
18+
constructor(
19+
@inject(IExtensionContext) private extensionContext: IExtensionContext,
20+
@inject(IDeepnoteNotebookManager) private readonly notebookManager: IDeepnoteNotebookManager
21+
) {}
1822

1923
/**
2024
* Activates Deepnote support by registering serializers and commands.
2125
* Called during extension activation to set up Deepnote integration.
2226
*/
2327
public activate() {
24-
this.serializer = new DeepnoteNotebookSerializer();
25-
this.explorerView = new DeepnoteExplorerView(this.extensionContext);
28+
this.serializer = new DeepnoteNotebookSerializer(this.notebookManager);
29+
this.explorerView = new DeepnoteExplorerView(this.extensionContext, this.notebookManager);
2630

2731
this.extensionContext.subscriptions.push(workspace.registerNotebookSerializer('deepnote', this.serializer));
2832

src/notebooks/deepnote/deepnoteActivationService.unit.test.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
import { assert } from 'chai';
22

33
import { DeepnoteActivationService } from './deepnoteActivationService';
4+
import { DeepnoteNotebookManager } from './deepnoteNotebookManager';
45
import { IExtensionContext } from '../../platform/common/types';
56

67
suite('DeepnoteActivationService', () => {
78
let activationService: DeepnoteActivationService;
89
let mockExtensionContext: IExtensionContext;
10+
let manager: DeepnoteNotebookManager;
911

1012
setup(() => {
1113
mockExtensionContext = {
1214
subscriptions: []
1315
} as any;
1416

15-
activationService = new DeepnoteActivationService(mockExtensionContext);
17+
manager = new DeepnoteNotebookManager();
18+
activationService = new DeepnoteActivationService(mockExtensionContext, manager);
1619
});
1720

1821
suite('constructor', () => {
@@ -70,8 +73,10 @@ suite('DeepnoteActivationService', () => {
7073
const context1 = { subscriptions: [] } as any;
7174
const context2 = { subscriptions: [] } as any;
7275

73-
const service1 = new DeepnoteActivationService(context1);
74-
const service2 = new DeepnoteActivationService(context2);
76+
const manager1 = new DeepnoteNotebookManager();
77+
const manager2 = new DeepnoteNotebookManager();
78+
const service1 = new DeepnoteActivationService(context1, manager1);
79+
const service2 = new DeepnoteActivationService(context2, manager2);
7580

7681
// Verify each service has its own context
7782
assert.strictEqual((service1 as any).extensionContext, context1);
@@ -94,8 +99,10 @@ suite('DeepnoteActivationService', () => {
9499
]
95100
} as any;
96101

97-
new DeepnoteActivationService(context1);
98-
new DeepnoteActivationService(context2);
102+
const manager1 = new DeepnoteNotebookManager();
103+
const manager2 = new DeepnoteNotebookManager();
104+
new DeepnoteActivationService(context1, manager1);
105+
new DeepnoteActivationService(context2, manager2);
99106

100107
assert.strictEqual(context1.subscriptions.length, 0);
101108
assert.strictEqual(context2.subscriptions.length, 1);

src/notebooks/deepnote/deepnoteExplorerView.ts

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,34 @@
11
import { injectable, inject } from 'inversify';
2-
import { window, commands, workspace, Uri } from 'vscode';
2+
import { commands, window, workspace, TreeView, Uri, l10n } from 'vscode';
33

44
import { IExtensionContext } from '../../platform/common/types';
5+
import { IDeepnoteNotebookManager } from '../types';
56
import { DeepnoteTreeDataProvider } from './deepnoteTreeDataProvider';
67
import { type DeepnoteTreeItem, DeepnoteTreeItemType, type DeepnoteTreeItemContext } from './deepnoteTreeItem';
7-
import { DeepnoteNotebookSerializer } from './deepnoteSerializer';
88

99
/**
1010
* Manages the Deepnote explorer tree view and related commands
1111
*/
1212
@injectable()
1313
export class DeepnoteExplorerView {
14-
private treeDataProvider: DeepnoteTreeDataProvider;
15-
private serializer: DeepnoteNotebookSerializer;
14+
private readonly treeDataProvider: DeepnoteTreeDataProvider;
1615

17-
constructor(@inject(IExtensionContext) private extensionContext: IExtensionContext) {
16+
private treeView: TreeView<DeepnoteTreeItem>;
17+
18+
constructor(
19+
@inject(IExtensionContext) private readonly extensionContext: IExtensionContext,
20+
@inject(IDeepnoteNotebookManager) private readonly manager: IDeepnoteNotebookManager
21+
) {
1822
this.treeDataProvider = new DeepnoteTreeDataProvider();
19-
this.serializer = new DeepnoteNotebookSerializer();
2023
}
2124

2225
public activate(): void {
23-
const treeView = window.createTreeView('deepnoteExplorer', {
26+
this.treeView = window.createTreeView('deepnoteExplorer', {
2427
treeDataProvider: this.treeDataProvider,
2528
showCollapseAll: true
2629
});
2730

28-
this.extensionContext.subscriptions.push(treeView);
31+
this.extensionContext.subscriptions.push(this.treeView);
2932
this.extensionContext.subscriptions.push(this.treeDataProvider);
3033

3134
this.registerCommands();
@@ -56,26 +59,36 @@ export class DeepnoteExplorerView {
5659
}
5760

5861
private async openNotebook(context: DeepnoteTreeItemContext): Promise<void> {
62+
console.log(`Opening notebook: ${context.notebookId} in project: ${context.projectId}.`);
63+
5964
if (!context.notebookId) {
65+
await window.showWarningMessage(l10n.t('Cannot open: missing notebook id.'));
66+
6067
return;
6168
}
6269

6370
try {
64-
const fileUri = Uri.file(context.filePath);
65-
const manager = this.serializer.getManager();
71+
// Create a unique URI by adding the notebook ID as a query parameter
72+
// This ensures VS Code treats each notebook as a separate document
73+
const fileUri = Uri.file(context.filePath).with({ query: `notebook=${context.notebookId}` });
6674

67-
manager.selectNotebookForProject(context.projectId, context.notebookId);
75+
console.log(`Selecting notebook in manager.`);
6876

69-
console.log(`Opening notebook ${context.notebookId} from project ${context.projectId}.`);
77+
this.manager.selectNotebookForProject(context.projectId, context.notebookId);
78+
79+
console.log(`Opening notebook document.`, fileUri);
7080

7181
const document = await workspace.openNotebookDocument(fileUri);
7282

83+
console.log(`Showing notebook document.`);
84+
7385
await window.showNotebookDocument(document, {
7486
preview: false,
7587
preserveFocus: false
7688
});
7789
} catch (error) {
7890
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
91+
7992
await window.showErrorMessage(`Failed to open notebook: ${errorMessage}`);
8093
}
8194
}
@@ -88,6 +101,7 @@ export class DeepnoteExplorerView {
88101
try {
89102
const fileUri = Uri.file(treeItem.context.filePath);
90103
const document = await workspace.openTextDocument(fileUri);
104+
91105
await window.showTextDocument(document);
92106
} catch (error) {
93107
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
@@ -111,10 +125,28 @@ export class DeepnoteExplorerView {
111125
return;
112126
}
113127

114-
await window.showInformationMessage(
115-
`Active notebook: ${notebookMetadata?.deepnoteNotebookName || 'Untitled'} in project ${
116-
notebookMetadata?.deepnoteProjectName || 'Untitled'
117-
}`
118-
);
128+
// Try to reveal the notebook in the explorer
129+
try {
130+
const treeItem = await this.treeDataProvider.findTreeItem(projectId, notebookId);
131+
132+
if (treeItem) {
133+
await this.treeView.reveal(treeItem, { select: true, focus: true, expand: true });
134+
} else {
135+
// Fall back to showing information if node not found
136+
await window.showInformationMessage(
137+
`Active notebook: ${notebookMetadata?.deepnoteNotebookName || 'Untitled'} in project ${
138+
notebookMetadata?.deepnoteProjectName || 'Untitled'
139+
}`
140+
);
141+
}
142+
} catch (error) {
143+
// Fall back to showing information if reveal fails
144+
console.error('Failed to reveal notebook in explorer:', error);
145+
await window.showInformationMessage(
146+
`Active notebook: ${notebookMetadata?.deepnoteNotebookName || 'Untitled'} in project ${
147+
notebookMetadata?.deepnoteProjectName || 'Untitled'
148+
}`
149+
);
150+
}
119151
}
120152
}

src/notebooks/deepnote/deepnoteExplorerView.unit.test.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
import { assert } from 'chai';
22

33
import { DeepnoteExplorerView } from './deepnoteExplorerView';
4-
import { DeepnoteTreeItemContext } from './deepnoteTreeItem';
5-
import { IExtensionContext } from '../../platform/common/types';
4+
import { DeepnoteNotebookManager } from './deepnoteNotebookManager';
5+
import type { DeepnoteTreeItemContext } from './deepnoteTreeItem';
6+
import type { IExtensionContext } from '../../platform/common/types';
67

78
suite('DeepnoteExplorerView', () => {
89
let explorerView: DeepnoteExplorerView;
910
let mockExtensionContext: IExtensionContext;
11+
let manager: DeepnoteNotebookManager;
1012

1113
setup(() => {
1214
mockExtensionContext = {
1315
subscriptions: []
1416
} as any;
1517

16-
explorerView = new DeepnoteExplorerView(mockExtensionContext);
18+
manager = new DeepnoteNotebookManager();
19+
explorerView = new DeepnoteExplorerView(mockExtensionContext, manager);
1720
});
1821

1922
suite('constructor', () => {
@@ -169,8 +172,10 @@ suite('DeepnoteExplorerView', () => {
169172
const context1 = { subscriptions: [] } as any;
170173
const context2 = { subscriptions: [] } as any;
171174

172-
const view1 = new DeepnoteExplorerView(context1);
173-
const view2 = new DeepnoteExplorerView(context2);
175+
const manager1 = new DeepnoteNotebookManager();
176+
const manager2 = new DeepnoteNotebookManager();
177+
const view1 = new DeepnoteExplorerView(context1, manager1);
178+
const view2 = new DeepnoteExplorerView(context2, manager2);
174179

175180
// Verify each view has its own context
176181
assert.strictEqual((view1 as any).extensionContext, context1);
@@ -190,7 +195,7 @@ suite('DeepnoteExplorerView', () => {
190195
const hasSerializer = (explorerView as any).serializer !== undefined;
191196

192197
// At least one component should be defined after construction
193-
assert.isTrue(hasTreeDataProvider || hasSerializer || true, 'Components are being initialized');
198+
assert.isTrue(hasTreeDataProvider || hasSerializer, 'Components are being initialized');
194199
});
195200
});
196201
});

src/notebooks/deepnote/deepnoteNotebookManager.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
1+
import { injectable } from 'inversify';
2+
3+
import { IDeepnoteNotebookManager } from '../types';
14
import type { DeepnoteProject } from './deepnoteTypes';
25

36
/**
47
* Centralized manager for tracking Deepnote notebook selections and project state.
58
* Manages per-project state including current selections and project data caching.
69
*/
7-
export class DeepnoteNotebookManager {
8-
private currentNotebookId = new Map<string, string>();
9-
private originalProjects = new Map<string, DeepnoteProject>();
10-
private selectedNotebookByProject = new Map<string, string>();
10+
@injectable()
11+
export class DeepnoteNotebookManager implements IDeepnoteNotebookManager {
12+
private readonly currentNotebookId = new Map<string, string>();
13+
private readonly originalProjects = new Map<string, DeepnoteProject>();
14+
private readonly selectedNotebookByProject = new Map<string, string>();
1115

1216
/**
1317
* Gets the currently selected notebook ID for a project.

0 commit comments

Comments
 (0)