Skip to content

Commit e00bbc1

Browse files
address Notebook breaking changes
1 parent ea0cf3a commit e00bbc1

File tree

7 files changed

+123
-59
lines changed

7 files changed

+123
-59
lines changed

.github/workflows/updateNotebookApi.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,6 @@ jobs:
7979
# Remove the old file so it doesn't get picked up by tsc
8080
Remove-Item ./old.vscode.proposed.d.ts -Force
8181
82-
- name: Compile the TypeScript to check for errors
83-
run: npm run compile
84-
8582
- name: Create Pull Request
8683
if: github.event_name == 'schedule'
8784
id: cpr

package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -218,17 +218,17 @@
218218
}
219219
},
220220
{
221-
"command": "PowerShell.ShowNotebookMode",
222-
"title": "(Preview) Show Notebook Mode",
221+
"command": "PowerShell.EnableNotebookMode",
222+
"title": "(Preview) Enable Notebook Mode",
223223
"category": "PowerShell",
224224
"icon": {
225225
"light": "resources/light/book.svg",
226226
"dark": "resources/dark/book.svg"
227227
}
228228
},
229229
{
230-
"command": "PowerShell.HideNotebookMode",
231-
"title": "Show Text Editor",
230+
"command": "PowerShell.DisableNotebookMode",
231+
"title": "(Preview) Disable Notebook Mode",
232232
"category": "PowerShell",
233233
"icon": {
234234
"light": "resources/light/file-code.svg",
@@ -413,12 +413,12 @@
413413
},
414414
{
415415
"when": "editorLangId == powershell && config.powershell.notebooks.showToggleButton",
416-
"command": "PowerShell.ShowNotebookMode",
416+
"command": "PowerShell.EnableNotebookMode",
417417
"group": "navigation@102"
418418
},
419419
{
420420
"when": "resourceLangId == powershell && notebookEditorFocused",
421-
"command": "PowerShell.HideNotebookMode",
421+
"command": "PowerShell.DisableNotebookMode",
422422
"group": "navigation@102"
423423
}
424424
],

src/features/PowerShellNotebooks.ts

Lines changed: 90 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -9,34 +9,69 @@ import { LanguageClientConsumer } from "../languageClientConsumer";
99
import Settings = require("../settings");
1010
import { ILogger } from "../logging";
1111

12-
export class PowerShellNotebooksFeature extends LanguageClientConsumer implements vscode.NotebookContentProvider, vscode.NotebookKernel {
12+
export class PowerShellNotebooksFeature extends LanguageClientConsumer {
1313

14-
private readonly showNotebookModeCommand: vscode.Disposable;
15-
private readonly hideNotebookModeCommand: vscode.Disposable;
14+
private readonly disposables: vscode.Disposable[];
15+
private readonly notebookContentProvider: vscode.NotebookContentProvider;
16+
private readonly notebookKernel: PowerShellNotebookKernel;
1617

17-
private _onDidChangeNotebook = new vscode.EventEmitter<vscode.NotebookDocumentEditEvent>();
18-
public onDidChangeNotebook: vscode.Event<vscode.NotebookDocumentEditEvent> = this._onDidChangeNotebook.event;
19-
public kernel?: vscode.NotebookKernel;
18+
public constructor(logger: ILogger, skipRegisteringCommands?: boolean) {
19+
super();
20+
this.disposables = [];
21+
if(!skipRegisteringCommands) {
22+
this.disposables.push(vscode.commands.registerCommand(
23+
"PowerShell.EnableNotebookMode",
24+
PowerShellNotebooksFeature.EnableNotebookMode));
2025

21-
public label: string = "PowerShell";
22-
public preloads?: vscode.Uri[];
26+
this.disposables.push(vscode.commands.registerCommand(
27+
"PowerShell.DisableNotebookMode",
28+
PowerShellNotebooksFeature.DisableNotebookMode));
29+
}
2330

24-
public constructor(private logger: ILogger, skipRegisteringCommands?: boolean) {
25-
super();
26-
// VS Code Notebook API uses this property for handling cell execution.
27-
this.kernel = this;
31+
this.notebookContentProvider = new PowerShellNotebookContentProvider(logger);
32+
this.notebookKernel = new PowerShellNotebookKernel();
33+
}
2834

29-
if(!skipRegisteringCommands) {
30-
this.showNotebookModeCommand = vscode.commands.registerCommand(
31-
"PowerShell.ShowNotebookMode",
32-
PowerShellNotebooksFeature.showNotebookMode);
35+
public registerNotebookProviders() {
36+
this.disposables.push(vscode.notebook.registerNotebookKernelProvider({
37+
viewType: "PowerShellNotebookMode"
38+
}, this.notebookKernel));
39+
40+
this.disposables.push(vscode.notebook.registerNotebookContentProvider(
41+
"PowerShellNotebookMode",
42+
this.notebookContentProvider));
43+
}
3344

34-
this.hideNotebookModeCommand = vscode.commands.registerCommand(
35-
"PowerShell.HideNotebookMode",
36-
PowerShellNotebooksFeature.hideNotebookMode);
45+
public dispose() {
46+
for (const disposable of this.disposables) {
47+
disposable.dispose();
3748
}
3849
}
3950

51+
public setLanguageClient(languageClient: LanguageClient) {
52+
this.notebookKernel.setLanguageClient(languageClient);
53+
}
54+
55+
private static async EnableNotebookMode() {
56+
const uri = vscode.window.activeTextEditor.document.uri;
57+
await vscode.commands.executeCommand("workbench.action.closeActiveEditor");
58+
await vscode.commands.executeCommand("vscode.openWith", uri, "PowerShellNotebookMode");
59+
}
60+
61+
private static async DisableNotebookMode() {
62+
const uri = vscode.notebook.activeNotebookEditor.document.uri;
63+
await vscode.commands.executeCommand("workbench.action.closeActiveEditor");
64+
await vscode.commands.executeCommand("vscode.openWith", uri, "default");
65+
}
66+
}
67+
68+
class PowerShellNotebookContentProvider implements vscode.NotebookContentProvider {
69+
private _onDidChangeNotebook = new vscode.EventEmitter<vscode.NotebookDocumentEditEvent>();
70+
public onDidChangeNotebook: vscode.Event<vscode.NotebookDocumentEditEvent> = this._onDidChangeNotebook.event;
71+
72+
public constructor(private logger: ILogger) {
73+
}
74+
4075
public async openNotebook(uri: vscode.Uri, context: vscode.NotebookDocumentOpenContext): Promise<vscode.NotebookData> {
4176
// load from backup if needed.
4277
const actualUri = context.backupId ? vscode.Uri.parse(context.backupId) : uri;
@@ -186,11 +221,6 @@ export class PowerShellNotebooksFeature extends LanguageClientConsumer implement
186221
};
187222
}
188223

189-
public dispose() {
190-
this.showNotebookModeCommand.dispose();
191-
this.hideNotebookModeCommand.dispose();
192-
}
193-
194224
private async _save(document: vscode.NotebookDocument, targetResource: vscode.Uri, _token: vscode.CancellationToken): Promise<void> {
195225
this.logger.writeDiagnostic(`Saving Notebook: ${targetResource.toString()}`);
196226

@@ -215,35 +245,51 @@ export class PowerShellNotebooksFeature extends LanguageClientConsumer implement
215245

216246
await vscode.workspace.fs.writeFile(targetResource, new TextEncoder().encode(retArr.join("\n")));
217247
}
248+
}
218249

219-
private static async showNotebookMode() {
220-
const uri = vscode.window.activeTextEditor.document.uri;
221-
await vscode.commands.executeCommand("workbench.action.closeActiveEditor");
222-
await vscode.commands.executeCommand("vscode.openWith", uri, "PowerShellNotebookMode");
223-
}
250+
class PowerShellNotebookKernel implements vscode.NotebookKernel, vscode.NotebookKernelProvider {
251+
public id?: string;
252+
public label: string = "PowerShell";
253+
public description?: string = "The PowerShell Notebook Mode kernel that runs commands in the PowerShell Integrated Console.";
254+
public isPreferred?: boolean;
255+
public preloads?: vscode.Uri[];
224256

225-
private static async hideNotebookMode() {
226-
const uri = vscode.notebook.activeNotebookEditor.document.uri;
227-
await vscode.commands.executeCommand("workbench.action.closeActiveEditor");
228-
await vscode.commands.executeCommand("vscode.openWith", uri, "default");
229-
}
257+
private languageClient: LanguageClient;
230258

231-
/*
232-
`vscode.NotebookKernel` implementations
233-
*/
234-
public async executeAllCells(document: vscode.NotebookDocument, token: vscode.CancellationToken): Promise<void> {
259+
public async executeAllCells(document: vscode.NotebookDocument): Promise<void> {
235260
for (const cell of document.cells) {
236-
await this.executeCell(document, cell, token);
261+
if (cell.cellKind === vscode.CellKind.Code) {
262+
await this.executeCell(document, cell);
263+
}
237264
}
238265
}
239266

240-
public async executeCell(document: vscode.NotebookDocument, cell: vscode.NotebookCell | undefined, token: vscode.CancellationToken): Promise<void> {
241-
if (token.isCancellationRequested) {
242-
return;
243-
}
244-
267+
public async executeCell(document: vscode.NotebookDocument, cell: vscode.NotebookCell | undefined): Promise<void> {
245268
await this.languageClient.sendRequest(EvaluateRequestType, {
246269
expression: cell.document.getText(),
247270
});
248271
}
272+
273+
// Since executing a cell is a "fire and forget", there's no time for the user to cancel
274+
// any of the executing cells. We can bring this in after PSES has a better API for executing code.
275+
public cancelCellExecution(document: vscode.NotebookDocument, cell: vscode.NotebookCell): void {
276+
return;
277+
}
278+
279+
// Since executing a cell is a "fire and forget", there's no time for the user to cancel
280+
// any of the executing cells. We can bring this in after PSES has a better API for executing code.
281+
public cancelAllCellsExecution(document: vscode.NotebookDocument): void {
282+
return;
283+
}
284+
285+
public setLanguageClient(languageClient: LanguageClient) {
286+
this.languageClient = languageClient;
287+
}
288+
289+
/*
290+
vscode.NotebookKernelProvider implementation
291+
*/
292+
public provideKernels(document: vscode.NotebookDocument, token: vscode.CancellationToken): vscode.ProviderResult<vscode.NotebookKernel[]> {
293+
return [this];
294+
}
249295
}

src/main.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,13 +170,17 @@ export function activate(context: vscode.ExtensionContext): void {
170170
const powerShellNotebooksFeature = new PowerShellNotebooksFeature(logger);
171171

172172
try {
173-
context.subscriptions.push(vscode.notebook.registerNotebookContentProvider("PowerShellNotebookMode", powerShellNotebooksFeature));
173+
powerShellNotebooksFeature.registerNotebookProviders();
174174
languageClientConsumers.push(powerShellNotebooksFeature);
175175
} catch (e) {
176176
// This would happen if VS Code changes their API.
177177
powerShellNotebooksFeature.dispose();
178178
logger.writeVerbose("Failed to register NotebookContentProvider", e);
179179
}
180+
} else {
181+
vscode.commands.registerCommand(
182+
"PowerShell.EnableNotebookMode",
183+
() => vscode.window.showWarningMessage("Notebook Mode only works in Visual Studio Code Insiders. To get it, go to: aka.ms/vscode-insiders"));
180184
}
181185

182186
sessionManager.setLanguageClientConsumers(languageClientConsumers);

test/.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"powershell.helpCompletion": "LineComment"
3+
}

test/features/PowerShellNotebooks.test.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,10 +192,12 @@ suite("PowerShellNotebooks tests", () => {
192192
]);
193193

194194
const feature = new PowerShellNotebooksFeature(new MockLogger(), true);
195+
// `notebookContentProvider` is a private property so cast the feature as `any` so we can access it.
196+
const notebookContentProvider: vscode.NotebookContentProvider = (feature as any).notebookContentProvider;
195197

196198
for (const [uri, expectedCells] of notebookTestData) {
197199
test(`Can open a notebook with expected cells - ${uri.fsPath}`, async () => {
198-
const actualNotebookData = await feature.openNotebook(uri, {});
200+
const actualNotebookData = await notebookContentProvider.openNotebook(uri, {});
199201
compareCells(actualNotebookData.cells, expectedCells);
200202
});
201203
}
@@ -218,8 +220,8 @@ suite("PowerShellNotebooks tests", () => {
218220
notebookSimpleMixedComments.toString());
219221

220222
// Save it as testFile.ps1 and reopen it using the feature.
221-
await feature.saveNotebookAs(uri, vscode.notebook.activeNotebookEditor.document, null);
222-
const newNotebook = await feature.openNotebook(uri, {});
223+
await notebookContentProvider.saveNotebookAs(uri, vscode.notebook.activeNotebookEditor.document, null);
224+
const newNotebook = await notebookContentProvider.openNotebook(uri, {});
223225

224226
// Compare that saving as a file results in the same cell data as the existing one.
225227
const expectedCells = notebookTestData.get(notebookSimpleMixedComments);

vscode.proposed.d.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ declare module 'vscode' {
8989
Error = 4
9090
}
9191

92+
export enum NotebookRunState {
93+
Running = 1,
94+
Idle = 2
95+
}
96+
9297
export interface NotebookCellMetadata {
9398
/**
9499
* Controls if the content of a cell is editable or not.
@@ -191,6 +196,11 @@ declare module 'vscode' {
191196
* Additional attributes of the document metadata.
192197
*/
193198
custom?: { [key: string]: any };
199+
200+
/**
201+
* The document's current run state
202+
*/
203+
runState?: NotebookRunState;
194204
}
195205

196206
export interface NotebookDocument {
@@ -498,8 +508,10 @@ declare module 'vscode' {
498508
description?: string;
499509
isPreferred?: boolean;
500510
preloads?: Uri[];
501-
executeCell(document: NotebookDocument, cell: NotebookCell, token: CancellationToken): Promise<void>;
502-
executeAllCells(document: NotebookDocument, token: CancellationToken): Promise<void>;
511+
executeCell(document: NotebookDocument, cell: NotebookCell): void;
512+
cancelCellExecution(document: NotebookDocument, cell: NotebookCell): void;
513+
executeAllCells(document: NotebookDocument): void;
514+
cancelAllCellsExecution(document: NotebookDocument): void;
503515
}
504516

505517
export interface NotebookDocumentFilter {

0 commit comments

Comments
 (0)