|
1 | 1 | import type { DeepnoteBlock, DeepnoteFile, DeepnoteSnapshot } from '@deepnote/blocks'; |
2 | 2 | import { deserializeDeepnoteFile, isExecutableBlock, serializeDeepnoteSnapshot } from '@deepnote/blocks'; |
3 | 3 | import { inject, injectable, optional } from 'inversify'; |
4 | | -import { l10n, window, workspace, type CancellationToken, type NotebookData, type NotebookSerializer } from 'vscode'; |
| 4 | +import { l10n, workspace, type CancellationToken, type NotebookData, type NotebookSerializer } from 'vscode'; |
5 | 5 |
|
6 | 6 | import { logger } from '../../platform/logging'; |
7 | 7 | import { IDeepnoteNotebookManager } from '../types'; |
@@ -62,7 +62,8 @@ export class DeepnoteNotebookSerializer implements NotebookSerializer { |
62 | 62 | /** |
63 | 63 | * Deserializes a Deepnote YAML file into VS Code notebook format. |
64 | 64 | * Parses YAML and converts the selected notebook's blocks to cells. |
65 | | - * The notebook to deserialize must be pre-selected and stored in the manager. |
| 65 | + * Notebook resolution prefers an explicit notebook ID, then transient |
| 66 | + * resolver state, and finally a deterministic default notebook. |
66 | 67 | * @param content Raw file content as bytes |
67 | 68 | * @param token Cancellation token (unused) |
68 | 69 | * @returns Promise resolving to notebook data |
@@ -186,37 +187,29 @@ export class DeepnoteNotebookSerializer implements NotebookSerializer { |
186 | 187 | } |
187 | 188 |
|
188 | 189 | /** |
189 | | - * Finds the notebook ID to deserialize by checking the manager's stored selection. |
190 | | - * The notebook ID should be set via selectNotebookForProject before opening the document. |
| 190 | + * Finds the notebook ID to deserialize without relying on active-editor state. |
| 191 | + * Prefers a pending resolution hint, then current/open-document state. |
191 | 192 | * @param projectId The project ID to find a notebook for |
192 | 193 | * @returns The notebook ID to deserialize, or undefined if none found |
193 | 194 | */ |
194 | 195 | findCurrentNotebookId(projectId: string): string | undefined { |
195 | | - // Check the manager's stored selection first - this is set explicitly when the user |
196 | | - // picks a notebook from the explorer, and must take priority over the active editor |
197 | | - const storedNotebookId = this.notebookManager.getTheSelectedNotebookForAProject(projectId); |
| 196 | + const pendingNotebookId = this.notebookManager.consumePendingNotebookResolution(projectId); |
| 197 | + const openNotebookIds = this.findOpenNotebookIds(projectId); |
| 198 | + const currentNotebookId = this.notebookManager.getCurrentNotebookId(projectId); |
198 | 199 |
|
199 | | - if (storedNotebookId) { |
200 | | - return storedNotebookId; |
| 200 | + if (pendingNotebookId) { |
| 201 | + return pendingNotebookId; |
201 | 202 | } |
202 | 203 |
|
203 | | - // Fallback: prefer the active notebook editor when it matches the project |
204 | | - const activeEditorNotebook = window.activeNotebookEditor?.notebook; |
205 | | - |
206 | | - if ( |
207 | | - activeEditorNotebook?.notebookType === 'deepnote' && |
208 | | - activeEditorNotebook.metadata?.deepnoteProjectId === projectId && |
209 | | - activeEditorNotebook.metadata?.deepnoteNotebookId |
210 | | - ) { |
211 | | - return activeEditorNotebook.metadata.deepnoteNotebookId; |
| 204 | + if (currentNotebookId && (openNotebookIds.length === 0 || openNotebookIds.includes(currentNotebookId))) { |
| 205 | + return currentNotebookId; |
212 | 206 | } |
213 | 207 |
|
214 | | - // Last fallback: Check if there's an active notebook document for this project |
215 | | - const openNotebook = workspace.notebookDocuments.find( |
216 | | - (doc) => doc.notebookType === 'deepnote' && doc.metadata?.deepnoteProjectId === projectId |
217 | | - ); |
| 208 | + if (openNotebookIds.length === 1) { |
| 209 | + return openNotebookIds[0]; |
| 210 | + } |
218 | 211 |
|
219 | | - return openNotebook?.metadata?.deepnoteNotebookId; |
| 212 | + return undefined; |
220 | 213 | } |
221 | 214 |
|
222 | 215 | /** |
@@ -264,7 +257,7 @@ export class DeepnoteNotebookSerializer implements NotebookSerializer { |
264 | 257 | logger.debug('SerializeNotebook: Got and cloned original project'); |
265 | 258 |
|
266 | 259 | const notebookId = |
267 | | - data.metadata?.deepnoteNotebookId || this.notebookManager.getTheSelectedNotebookForAProject(projectId); |
| 260 | + data.metadata?.deepnoteNotebookId || this.notebookManager.getCurrentNotebookId(projectId); |
268 | 261 |
|
269 | 262 | if (!notebookId) { |
270 | 263 | throw new Error('Cannot determine which notebook to save'); |
@@ -353,6 +346,11 @@ export class DeepnoteNotebookSerializer implements NotebookSerializer { |
353 | 346 |
|
354 | 347 | // Store the updated project back so subsequent saves start from correct state |
355 | 348 | this.notebookManager.storeOriginalProject(projectId, originalProject, notebookId); |
| 349 | + const openNotebookIdsAtSerialize = this.findOpenNotebookIds(projectId); |
| 350 | + |
| 351 | + if (openNotebookIdsAtSerialize.length === 0) { |
| 352 | + this.notebookManager.queueNotebookResolution(projectId, notebookId); |
| 353 | + } |
356 | 354 |
|
357 | 355 | logger.debug('SerializeNotebook: Serializing to YAML'); |
358 | 356 |
|
@@ -555,6 +553,21 @@ export class DeepnoteNotebookSerializer implements NotebookSerializer { |
555 | 553 | return false; |
556 | 554 | } |
557 | 555 |
|
| 556 | + private findOpenNotebookIds(projectId: string): string[] { |
| 557 | + return [ |
| 558 | + ...new Set( |
| 559 | + workspace.notebookDocuments |
| 560 | + .filter( |
| 561 | + (doc) => |
| 562 | + doc.notebookType === 'deepnote' && |
| 563 | + doc.metadata?.deepnoteProjectId === projectId && |
| 564 | + typeof doc.metadata?.deepnoteNotebookId === 'string' |
| 565 | + ) |
| 566 | + .map((doc) => doc.metadata.deepnoteNotebookId as string) |
| 567 | + ) |
| 568 | + ]; |
| 569 | + } |
| 570 | + |
558 | 571 | /** |
559 | 572 | * Finds the default notebook to open when no selection is made. |
560 | 573 | * @param file |
|
0 commit comments