Skip to content

Commit d1db013

Browse files
subhash-arabhiAchal1607
authored andcommitted
Introduced mimeTypeHandler and executionSummary and refactored code
1 parent 44acce0 commit d1db013

File tree

5 files changed

+147
-40
lines changed

5 files changed

+147
-40
lines changed

vscode/src/notebooks/codeCellExecution.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { commands, NotebookCell, NotebookCellExecution, NotebookCellOutput, NotebookController } from "vscode";
22
import { LOGGER } from "../logger";
33
import { NotebookCellExecutionResult } from "../lsp/protocol";
4-
import { createErrorOutputItem, createOutputItem } from "./utils";
4+
import { createErrorOutputItem } from "./utils";
55
import { nbCommands } from "../commands/commands";
66
import { mimeTypes } from "./constants";
7+
import { MimeTypeHandler } from "./mimeTypeHandler";
78

89
export class CodeCellExecution {
910
private controller?: NotebookController;
@@ -74,7 +75,7 @@ export class CodeCellExecution {
7475
await this.execution!.replaceOutputItems(createErrorOutputItem(updatedData), this.output);
7576
} else {
7677
await this.execution!.replaceOutputItems(
77-
createOutputItem(updatedData, mimeType),
78+
new MimeTypeHandler(mimeType).makeOutputItem(updatedData),
7879
this.output
7980
);
8081
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates.
3+
*
4+
* Licensed to the Apache Software Foundation (ASF) under one
5+
* or more contributor license agreements. See the NOTICE file
6+
* distributed with this work for additional information
7+
* regarding copyright ownership. The ASF licenses this file
8+
* to you under the Apache License, Version 2.0 (the
9+
* "License"); you may not use this file except in compliance
10+
* with the License. You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing,
15+
* software distributed under the License is distributed on an
16+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
* KIND, either express or implied. See the License for the
18+
* specific language governing permissions and limitations
19+
* under the License.
20+
*/
21+
22+
export interface ExecutionSummaryData {
23+
executionOrder?: number | null;
24+
success?: boolean;
25+
}
26+
27+
export class ExecutionSummary {
28+
constructor(
29+
public executionOrder: number | null = null,
30+
public success: boolean = false
31+
) {}
32+
33+
static fromMetadata(
34+
meta?: ExecutionSummaryData,
35+
fallbackExecCount?: number | null
36+
): ExecutionSummary {
37+
const order =
38+
meta?.executionOrder != null
39+
? meta.executionOrder
40+
: fallbackExecCount ?? null;
41+
const success = meta?.success ?? false;
42+
return new ExecutionSummary(order, success);
43+
}
44+
45+
toJSON(): ExecutionSummaryData {
46+
return {
47+
executionOrder: this.executionOrder,
48+
success: this.success,
49+
};
50+
}
51+
}

vscode/src/notebooks/kernel.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,16 @@
2222
import { globalState } from '../globalState';
2323
import { isNbCommandRegistered } from '../commands/utils';
2424
import { nbCommands } from '../commands/commands';
25-
import { createErrorOutput, createOutputItem } from './utils';
2625
import { NotebookCellExecutionResult } from '../lsp/protocol';
2726
import { NotebookCell, NotebookController, NotebookDocument, Disposable, notebooks, commands, NotebookCellOutput, workspace } from 'vscode';
2827
import { LanguageClient } from 'vscode-languageclient/node';
2928
import { l10n } from '../localiser';
3029
import { ijnbConstants, ipynbConstants, supportLanguages } from './constants';
3130
import { LOGGER } from '../logger';
3231
import { CodeCellExecution } from './codeCellExecution';
33-
import { isError } from '../utils';
32+
import { isError, isString } from '../utils';
33+
import { MimeTypeHandler } from './mimeTypeHandler';
34+
import { createErrorOutput } from './utils';
3435

3536
export class IJNBKernel implements Disposable {
3637
private readonly controllers: NotebookController[] = [];
@@ -165,7 +166,7 @@ export class IJNBKernel implements Disposable {
165166
try {
166167
exec.start(Date.now());
167168
await exec.replaceOutput([
168-
new NotebookCellOutput([createOutputItem(cell.document.getText(), mimeType)]),
169+
new NotebookCellOutput([new MimeTypeHandler(mimeType).makeOutputItem(cell.document.getText())]),
169170
]);
170171
exec.end(true, Date.now());
171172
} catch (error) {
@@ -175,10 +176,10 @@ export class IJNBKernel implements Disposable {
175176
}
176177

177178
cleanUpKernel = workspace.onDidCloseNotebookDocument(doc => {
178-
if (doc.notebookType === ijnbConstants.NOTEBOOK_TYPE) {
179-
IJNBKernel.executionCounter.delete(doc.uri.toString());
180-
}
181-
});
179+
if (doc.notebookType === ijnbConstants.NOTEBOOK_TYPE) {
180+
IJNBKernel.executionCounter.delete(doc.uri.toString());
181+
}
182+
});
182183
}
183184

184185
export const notebookKernel = new IJNBKernel();
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates.
3+
*
4+
* Licensed to the Apache Software Foundation (ASF) under one
5+
* or more contributor license agreements. See the NOTICE file
6+
* distributed with this work for additional information
7+
* regarding copyright ownership. The ASF licenses this file
8+
* to you under the Apache License, Version 2.0 (the
9+
* "License"); you may not use this file except in compliance
10+
* with the License. You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing,
15+
* software distributed under the License is distributed on an
16+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
* KIND, either express or implied. See the License for the
18+
* specific language governing permissions and limitations
19+
* under the License.
20+
*/
21+
22+
import { Buffer } from 'buffer';
23+
import * as vscode from 'vscode';
24+
import { IMimeBundle } from './types';
25+
import { mimeTypes } from './constants';
26+
27+
export type DataOrBytes = string | Uint8Array;
28+
29+
export class MimeTypeHandler {
30+
constructor(public readonly value: string) {}
31+
get isText(): boolean {
32+
return this.value === mimeTypes.TEXT;
33+
}
34+
get isImage(): boolean {
35+
return this.value.startsWith('image/');
36+
}
37+
38+
static toBytes(data: DataOrBytes): Uint8Array {
39+
if (typeof data === 'string') {
40+
return Buffer.from(data, 'base64');
41+
}
42+
return data;
43+
}
44+
45+
static toString(data: DataOrBytes): string {
46+
if (typeof data === 'string') {
47+
return data;
48+
}
49+
return new TextDecoder().decode(data);
50+
}
51+
52+
makeOutputItem(data: DataOrBytes): vscode.NotebookCellOutputItem {
53+
if (this.isImage) {
54+
const bytes = MimeTypeHandler.toBytes(data);
55+
return new vscode.NotebookCellOutputItem(bytes, this.value);
56+
}
57+
const text = MimeTypeHandler.toString(data);
58+
return vscode.NotebookCellOutputItem.text(text, this.value);
59+
}
60+
61+
static itemsFromBundle(bundle: IMimeBundle): vscode.NotebookCellOutputItem[] {
62+
return Object.entries(bundle).flatMap(([mime, data]) => {
63+
const mt = new MimeTypeHandler(mime);
64+
if (mt.isText || mt.isImage) {
65+
const payload = Array.isArray(data) ? data.join('') : data;
66+
return [mt.makeOutputItem(payload)];
67+
}
68+
return [];
69+
});
70+
}
71+
}

vscode/src/notebooks/utils.ts

Lines changed: 14 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ import {
3333
import { randomUUID } from 'crypto';
3434
import { isString } from '../utils';
3535
import { mimeTypes } from './constants';
36+
import { MimeTypeHandler } from './mimeTypeHandler';
37+
import { ExecutionSummary } from './executionSummary';
38+
3639

3740
export function base64ToUint8Array(base64: string): Uint8Array {
3841
if (typeof Buffer !== 'undefined' && typeof Buffer.from === 'function') {
@@ -56,15 +59,6 @@ export function uint8ArrayToBase64(data: Uint8Array): string {
5659
return btoa(binary);
5760
}
5861

59-
export function createOutputItem(data: string | Uint8Array, mimeType: string): vscode.NotebookCellOutputItem {
60-
if (mimeType.startsWith('image/')) {
61-
const bytes = typeof data === 'string' ? base64ToUint8Array(data) : data;
62-
return new vscode.NotebookCellOutputItem(bytes, mimeType);
63-
}
64-
const text = typeof data === 'string' ? data : new TextDecoder().decode(data);
65-
return vscode.NotebookCellOutputItem.text(text, mimeType);
66-
}
67-
6862
export const createErrorOutput = (err: string | Error) => {
6963
return new vscode.NotebookCellOutput([createErrorOutputItem(err)]);
7064
}
@@ -86,14 +80,13 @@ export function parseCell(cell: ICell): vscode.NotebookCellData {
8680
const cellData = new vscode.NotebookCellData(kind, value, language);
8781
cellData.metadata = { id: cell.id, ...cell.metadata };
8882
if (cell.cell_type === 'code') {
89-
90-
const metaExec = (cell.metadata as IMetadata).executionSummary;
91-
const executionOrder = metaExec?.executionOrder ?? cell.execution_count ?? undefined;
92-
const success = metaExec?.success ?? undefined;
93-
83+
const execSummary = ExecutionSummary.fromMetadata(
84+
(cell.metadata as IMetadata).executionSummary,
85+
cell.execution_count ?? null
86+
);
9487
cellData.executionSummary = {
95-
executionOrder,
96-
success,
88+
executionOrder: execSummary.executionOrder ?? undefined,
89+
success: execSummary.success,
9790
};
9891

9992
if (Array.isArray(cell.outputs)) {
@@ -119,7 +112,7 @@ export function parseOutput(raw: IOutput): vscode.NotebookCellOutput[] {
119112
case 'stream':
120113
outputs.push(
121114
new vscode.NotebookCellOutput([
122-
vscode.NotebookCellOutputItem.text(Array.isArray(raw.text) ? raw.text.join('') : raw.text),
115+
new MimeTypeHandler(mimeTypes.TEXT).makeOutputItem(Array.isArray(raw.text) ? raw.text.join('') : raw.text),
123116
])
124117
);
125118
break;
@@ -138,19 +131,8 @@ export function parseOutput(raw: IOutput): vscode.NotebookCellOutput[] {
138131

139132
case 'display_data':
140133
case 'execute_result':
141-
const items: vscode.NotebookCellOutputItem[] = [];
142-
const bundle = raw.data || {};
143-
for (const mime in bundle) {
144-
const data = bundle[mime];
145-
if (mime === mimeTypes.TEXT) {
146-
const text = Array.isArray(data) ? data.join('') : String(data);
147-
items.push(vscode.NotebookCellOutputItem.text(text));
148-
} else if ((mime as string).startsWith('image/')) {
149-
const b64 = Array.isArray(data) ? data.join('') : String(data);
150-
const bytes = base64ToUint8Array(b64);
151-
items.push(new vscode.NotebookCellOutputItem(bytes, mime));
152-
}
153-
}
134+
const bundle = raw.data ?? {};
135+
const items = MimeTypeHandler.itemsFromBundle(bundle);
154136
if (items.length) {
155137
outputs.push(new vscode.NotebookCellOutput(items, raw.metadata));
156138
}
@@ -168,7 +150,8 @@ export function serializeCell(cell: vscode.NotebookCellData): ICell {
168150
const executionCount = exec.executionOrder ?? null;
169151
const success = exec.success ?? false;
170152

171-
const metadata = { ...baseMeta, executionSummary: { executionOrder: executionCount, success } };
153+
const execSummary = new ExecutionSummary(executionCount, success);
154+
const metadata = { ...baseMeta, executionSummary: execSummary.toJSON()};
172155

173156
const outputs: IOutput[] = (cell.outputs || []).map((output): IOutput => {
174157
const data: IMimeBundle = {};

0 commit comments

Comments
 (0)