Skip to content

Commit 3e516a0

Browse files
brettfocolombod
authored andcommitted
allow /openNotebook handler to read from a url
1 parent 4cf5153 commit 3e516a0

File tree

5 files changed

+100
-42
lines changed

5 files changed

+100
-42
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ There are several ways to get started using .NET with Jupyter, including Jupyter
3939

4040
You can find additional documentation [here](./docs/README.md).
4141

42+
## Test Script
43+
44+
Open the VS Code extension test script in [VS Code - Insiders](vscode-insiders://ms-dotnettools.dotnet-interactive-vscode/openNotebook?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdotnet%2Finteractive%2Fmain%2FNotebookTestScript.dib).
45+
4246
## Packages
4347

4448
We provide a number of packages that can be used to write custom [extensions](./docs/extending-dotnet-interactive.md) for .NET Interactive or to build your own interactive experiences.

src/dotnet-interactive-vscode/common/vscode/extension.ts

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { StdioKernelTransport } from '../stdioKernelTransport';
1313
import { registerLanguageProviders } from './languageProvider';
1414
import { registerAcquisitionCommands, registerKernelCommands, registerFileCommands } from './commands';
1515

16-
import { getSimpleLanguage, isDotnetInteractiveLanguage, isJupyterNotebookViewType } from '../interactiveNotebook';
16+
import { getSimpleLanguage, isDotnetInteractiveLanguage, isJupyterNotebookViewType, jupyterViewType } from '../interactiveNotebook';
1717
import { InteractiveLaunchOptions, InstallInteractiveArgs } from '../interfaces';
1818

1919
import { executeSafe, getWorkingDirectoryForNotebook, isDotNetUpToDate, processArguments } from '../utilities';
@@ -62,36 +62,6 @@ export async function activate(context: vscode.ExtensionContext) {
6262
// this must happen early, because some following functions use the acquisition command
6363
registerAcquisitionCommands(context, diagnosticsChannel);
6464

65-
vscode.window.registerUriHandler({
66-
handleUri(uri: vscode.Uri): vscode.ProviderResult<void> {
67-
const params = new URLSearchParams(uri.query);
68-
switch (uri.path) {
69-
case '/newNotebook':
70-
// Examples:
71-
// vscode://ms-dotnettools.dotnet-interactive-vscode/newNotebook?as=dib
72-
// vscode://ms-dotnettools.dotnet-interactive-vscode/newNotebook?as=ipynb
73-
const asType = params.get('as');
74-
vscode.commands.executeCommand('dotnet-interactive.acquire').then(() => {
75-
const commandName = asType === 'ipynb'
76-
? 'dotnet-interactive.newNotebookIpynb'
77-
: 'dotnet-interactive.newNotebookDib';
78-
vscode.commands.executeCommand(commandName).then(() => { });
79-
});
80-
break;
81-
case '/openNotebook':
82-
// Example
83-
// vscode://ms-dotnettools.dotnet-interactive-vscode/openNotebook?path=C%3A%5Cpath%5Cto%5Cnotebook.dib
84-
const path = params.get('path');
85-
if (path) {
86-
vscode.commands.executeCommand('dotnet-interactive.acquire').then(() => {
87-
vscode.commands.executeCommand('dotnet-interactive.openNotebook', vscode.Uri.file(path)).then(() => { });
88-
});
89-
}
90-
break;
91-
}
92-
}
93-
});
94-
9565
async function kernelTransportCreator(notebookUri: vscodeLike.Uri): Promise<contracts.KernelTransport> {
9666
if (!await checkForDotNetSdk(minDotNetSdkVersion!)) {
9767
const message = 'Unable to find appropriate .NET SDK.';
@@ -150,6 +120,41 @@ export async function activate(context: vscode.ExtensionContext) {
150120
};
151121
const clientMapper = new ClientMapper(clientMapperConfig);
152122

123+
vscode.window.registerUriHandler({
124+
handleUri(uri: vscode.Uri): vscode.ProviderResult<void> {
125+
const params = new URLSearchParams(uri.query);
126+
switch (uri.path) {
127+
case '/newNotebook':
128+
// Examples:
129+
// vscode://ms-dotnettools.dotnet-interactive-vscode/newNotebook?as=dib
130+
// vscode://ms-dotnettools.dotnet-interactive-vscode/newNotebook?as=ipynb
131+
const asType = params.get('as');
132+
vscode.commands.executeCommand('dotnet-interactive.acquire').then(() => {
133+
const commandName = asType === 'ipynb'
134+
? 'dotnet-interactive.newNotebookIpynb'
135+
: 'dotnet-interactive.newNotebookDib';
136+
vscode.commands.executeCommand(commandName).then(() => { });
137+
});
138+
break;
139+
case '/openNotebook':
140+
// Open a local notebook
141+
// vscode://ms-dotnettools.dotnet-interactive-vscode/openNotebook?path=C%3A%5Cpath%5Cto%5Cnotebook.dib
142+
// New untitled notebook from remote source
143+
// vscode://ms-dotnettools.dotnet-interactive-vscode/openNotebook?url=http%3A%2F%2Fexample.com%2Fnotebook.dib
144+
const notebookPath = params.get('path');
145+
const url = params.get('url');
146+
if (notebookPath) {
147+
vscode.commands.executeCommand('dotnet-interactive.acquire').then(() => {
148+
vscode.commands.executeCommand('dotnet-interactive.openNotebook', vscode.Uri.file(notebookPath)).then(() => { });
149+
});
150+
} else if (url) {
151+
versionSpecificFunctions.openNotebookFromUrl(url, clientMapper, diagnosticsChannel).then(() => { });
152+
}
153+
break;
154+
}
155+
}
156+
});
157+
153158
registerKernelCommands(context, clientMapper);
154159

155160
const hostVersionSuffix = isInsidersBuild() ? 'Insiders' : 'Stable';

src/dotnet-interactive-vscode/insiders/src/notebookSerializers.ts

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,14 @@ import { createUri } from './common/utilities';
1616
abstract class DotNetNotebookSerializer implements vscode.NotebookSerializer {
1717

1818
private serializerId = '*DOTNET-INTERACTIVE-NOTEBOOK-SERIALIZATION*';
19-
private disposable: vscode.Disposable;
2019
private eol: Eol;
2120

2221
constructor(
23-
notebookType: string,
2422
private readonly clientMapper: ClientMapper,
2523
private readonly outputChannel: OutputChannelAdapter,
2624
private readonly extension: string,
2725
) {
2826
this.eol = getEol();
29-
this.disposable = vscode.notebook.registerNotebookSerializer(notebookType, this);
30-
}
31-
32-
dispose(): void {
33-
this.disposable.dispose();
3427
}
3528

3629
async deserializeNotebook(content: Uint8Array, token: vscode.CancellationToken): Promise<vscode.NotebookData> {
@@ -88,13 +81,31 @@ function toNotebookCell(cell: vscode.NotebookCellData): contracts.NotebookCell {
8881

8982
export class DotNetDibNotebookSerializer extends DotNetNotebookSerializer {
9083
constructor(clientMapper: ClientMapper, outputChannel: OutputChannelAdapter) {
91-
super('dotnet-interactive', clientMapper, outputChannel, '.dib');
84+
super(clientMapper, outputChannel, '.dib');
85+
}
86+
87+
static registerNotebookSerializer(context: vscode.ExtensionContext, notebookType: string, clientMapper: ClientMapper, outputChannel: OutputChannelAdapter) {
88+
const serializer = new DotNetDibNotebookSerializer(clientMapper, outputChannel);
89+
const notebookSerializer = vscode.notebook.registerNotebookSerializer(notebookType, serializer);
90+
context.subscriptions.push(notebookSerializer);
9291
}
9392
}
9493

9594
export class DotNetLegacyNotebookSerializer extends DotNetNotebookSerializer {
9695
constructor(clientMapper: ClientMapper, outputChannel: OutputChannelAdapter) {
97-
super('dotnet-interactive-legacy', clientMapper, outputChannel, '.dib');
96+
super(clientMapper, outputChannel, '.dib');
97+
}
98+
99+
static registerNotebookSerializer(context: vscode.ExtensionContext, notebookType: string, clientMapper: ClientMapper, outputChannel: OutputChannelAdapter) {
100+
const serializer = new DotNetLegacyNotebookSerializer(clientMapper, outputChannel);
101+
const notebookSerializer = vscode.notebook.registerNotebookSerializer(notebookType, serializer);
102+
context.subscriptions.push(notebookSerializer);
103+
}
104+
}
105+
106+
export class DotNetJupyterNotebookSerializer extends DotNetNotebookSerializer {
107+
constructor(clientMapper: ClientMapper, outputChannel: OutputChannelAdapter) {
108+
super(clientMapper, outputChannel, '.ipynb');
98109
}
99110
}
100111

src/dotnet-interactive-vscode/insiders/src/versionSpecificFunctions.ts

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) .NET Foundation and contributors. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

4+
import * as path from 'path';
45
import * as vscode from 'vscode';
56
import * as contracts from './common/interfaces/contracts';
67
import * as vscodeLike from './common/interfaces/vscode-like';
@@ -14,6 +15,7 @@ import * as notebookSerializers from './notebookSerializers';
1415
import { ClientMapper } from './common/clientMapper';
1516
import { OutputChannelAdapter } from './common/vscode/OutputChannelAdapter';
1617
import { ErrorOutputCreator } from './common/interactiveClient';
18+
import fetch from 'node-fetch';
1719

1820
export function registerWithVsCode(context: vscode.ExtensionContext, clientMapper: ClientMapper, outputChannel: OutputChannelAdapter, createErrorOutput: ErrorOutputCreator, ...preloadUris: vscode.Uri[]) {
1921
const config = {
@@ -22,8 +24,8 @@ export function registerWithVsCode(context: vscode.ExtensionContext, clientMappe
2224
createErrorOutput,
2325
};
2426
context.subscriptions.push(new notebookControllers.DotNetNotebookKernel(config));
25-
context.subscriptions.push(new notebookSerializers.DotNetDibNotebookSerializer(clientMapper, outputChannel));
26-
context.subscriptions.push(new notebookSerializers.DotNetLegacyNotebookSerializer(clientMapper, outputChannel));
27+
notebookSerializers.DotNetDibNotebookSerializer.registerNotebookSerializer(context, 'dotnet-interactive', clientMapper, outputChannel);
28+
notebookSerializers.DotNetLegacyNotebookSerializer.registerNotebookSerializer(context, 'dotnet-interactive-legacy', clientMapper, outputChannel);
2729
}
2830

2931
export function endExecution(cell: vscode.NotebookCell, success: boolean) {
@@ -80,3 +82,35 @@ export async function createNewBlankNotebook(extension: string, _openNotebook: (
8082
const notebook = await vscode.notebook.openNotebookDocument(viewType, content);
8183
const _editor = await vscode.window.showNotebookDocument(notebook);
8284
}
85+
86+
export async function openNotebookFromUrl(notebookUrl: string, clientMapper: ClientMapper, diagnosticsChannel: OutputChannelAdapter): Promise<void> {
87+
await vscode.commands.executeCommand('dotnet-interactive.acquire');
88+
const extension = path.extname(notebookUrl);
89+
let serializer: notebookSerializers.DotNetDibNotebookSerializer | undefined = undefined;
90+
let viewType: string | undefined = undefined;
91+
switch (extension) {
92+
case '.dib':
93+
case '.dotnet-interactive':
94+
serializer = new notebookSerializers.DotNetDibNotebookSerializer(clientMapper, diagnosticsChannel);
95+
viewType = 'dotnet-interactive';
96+
break;
97+
case '.ipynb':
98+
serializer = new notebookSerializers.DotNetJupyterNotebookSerializer(clientMapper, diagnosticsChannel);
99+
viewType = 'jupyter-notebook';
100+
break;
101+
}
102+
103+
if (serializer && viewType) {
104+
try {
105+
const response = await fetch(notebookUrl);
106+
const arrayBuffer = await response.arrayBuffer();
107+
const content = new Uint8Array(arrayBuffer);
108+
const cancellationTokenSource = new vscode.CancellationTokenSource();
109+
const notebookData = await serializer.deserializeNotebook(content, cancellationTokenSource.token);
110+
const notebook = await vscode.notebook.openNotebookDocument(viewType, notebookData);
111+
const _editor = await vscode.window.showNotebookDocument(notebook);
112+
} catch (e) {
113+
vscode.window.showWarningMessage(`Unable to read notebook from '${notebookUrl}': ${e}`);
114+
}
115+
}
116+
}

src/dotnet-interactive-vscode/stable/src/versionSpecificFunctions.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,7 @@ function getNewNotebookName(extension: string): string {
6262
} while (workspaceHasUnsavedNotebookWithName(filename));
6363
return filename;
6464
}
65+
66+
export async function openNotebookFromUrl(notebookUrl: string, clientMapper: ClientMapper, diagnosticsChannel: OutputChannelAdapter): Promise<void> {
67+
// NOOP
68+
}

0 commit comments

Comments
 (0)