Skip to content

Commit

Permalink
repl-kernel improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
redking00 committed Dec 2, 2024
1 parent 2efd339 commit 203b156
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 90 deletions.
10 changes: 1 addition & 9 deletions client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"dependencies": {
"@jupyterlab/nbformat": "^4.2.5",
"@lumino/coreutils": "^2.2.0",
"ansi-sequence-parser": "^1.1.1",
"strip-ansi": "^7.1.0",
"dotenv": "^16.4.5",
"jsonc-parser": "^3.2.0",
"semver": "7.5.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ globalThis.Deno.jupyter = (function () {
if (!options.raw) {
data = JSON.stringify(data);
}
console.log(`##DISPLAYDATA#2d522e5a-4a6c-4aae-b20c-91c5189948d9##${JSON.stringify(data)}`);
console.log(`##DISPLAYDATA#2d522e5a-4a6c-4aae-b20c-91c5189948d9##\n${JSON.stringify(data)}\n##ENDDISPLAYDATA#2d522e5a-4a6c-4aae-b20c-91c5189948d9##`);
}

function makeDisplayable(obj: unknown): Deno.jupyter.Displayable {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import * as vscode from "vscode";
import * as path from 'path';
import { ISession } from "../../types";
import { ChildProcess } from "child_process";
import { DenoTool } from "../../../tools";
import { TextEncoder } from "util";
import { parseAnsiSequences } from 'ansi-sequence-parser';
import { DenoTool } from "../../../tools";
import stripAnsi from 'strip-ansi';
import { UUID } from "@lumino/coreutils";
import { ChildProcess } from "child_process";

const CTRL_S = '\r';

export class REPLSession implements ISession {
private context: vscode.ExtensionContext;
private currentDocument: vscode.NotebookDocument;
private outputChannel: vscode.OutputChannel;
private stopped: boolean = false;
private started: boolean = false;
private proc: ChildProcess;
private proc: ChildProcess
private currentExecution?: vscode.NotebookCellExecution;

private static lineIsError(line: string): boolean {
Expand All @@ -23,37 +24,36 @@ export class REPLSession implements ISession {
|| line.startsWith('Uncaught ReferenceError: ');
}

private runCode(code: string): Promise<{ lines: string[], errors: string[] }> {
let resolver: (result: { lines: string[], errors: string[] }) => void;
const result = new Promise<{ lines: string[], errors: string[] }>((resolve) => resolver = resolve);
private async runCode(code: string, onDataLine?: (dataLines: string) => void): Promise<void> {
code = code.split(/\r?\n/).join(CTRL_S);
const executionId = UUID.uuid4();
let resultLines = [];
let errors = [];
const dataHandler = (data: string) => {
const rawLines = data.replaceAll('\r\n', '\n').split('\n');
const lineTokens = rawLines.map((l: string) => parseAnsiSequences(l));
const lines = lineTokens.map((tokens) => tokens.map((token) => token.value).join(''));
errors.push(...lines.filter((l: string) => REPLSession.lineIsError(l)));
let finished = false;
if (lines.length > 1 && lines[lines.length - 1] === '') {
if (lines[lines.length - 2] === `"${executionId}"`) {
lines.splice(-2, 2);
finished = true;
let resolver: () => void;
const dataPromise = new Promise<void>((resolve) => { resolver = resolve; });
this.proc.stdout?.on("data", (data) => {
const parts = data.split(/(\r\n|\r|\n)/);
let isFinish = false;
for (const line of parts) {
const cLine = stripAnsi(line);
let pushLine = true;
if (!isFinish) {
if (REPLSession.lineIsError(cLine)) {
isFinish = true;
}
else if (cLine.includes(executionId)) {
isFinish = true;
pushLine = false;
}
}
if (pushLine && onDataLine) onDataLine(line);
}
resultLines.push(...lines);
if (finished) {
this.proc.stdout!.removeListener("data", dataHandler);
resolver({
lines: resultLines,
errors: errors
});
if (isFinish) {
this.proc.stdout?.removeAllListeners();
resolver();
}
}
this.proc.stdout!.addListener("data", dataHandler);
code += `\n"${executionId}"\n`;
});
code += `${CTRL_S}"${executionId}"\n`;
this.proc.stdin!.write(new TextEncoder().encode(code));
return result;
return dataPromise;
}

private static processOutput(results: any) {
Expand All @@ -76,57 +76,68 @@ export class REPLSession implements ISession {

private async execute(exec: vscode.NotebookCellExecution): Promise<boolean> {
this.currentExecution = exec;
this.currentExecution.start();
this.currentExecution.clearOutput();
let resolver: (result: boolean) => void;
const result = new Promise<boolean>((resolve) => resolver = resolve);
const executionId = UUID.uuid4();
let errors = [];
const dataHandler = (data: string) => {
const rawLines = data.replaceAll('\r\n', '\n').split('\n');
const filteredLines = rawLines.filter((l) => l !== '\x1b[90mundefined\x1b[39m');
const lineTokens = filteredLines.map((l: string) => parseAnsiSequences(l));
const lines = lineTokens.map((tokens) => tokens.map((token) => token.value).join(''));
errors.push(...lines.filter((l: string) => REPLSession.lineIsError(l)));
let finished = false;
if (lines.length > 1 && lines[lines.length - 1] === '') {
if (lines[lines.length - 2] === `"${executionId}"`) {
lines.splice(-2, 2);
finished = true;
exec.start();
exec.clearOutput();
let code = exec.cell.document.getText();
const errors: string[] = [];
const dataLines: string[] = [];
const displayData: string[] = [];
let isDisplayData = false;
let hasOutput = false;
let outputs: vscode.NotebookCellOutput[] = [];


await this.runCode(code, async (dataLine) => {
const dl = stripAnsi(dataLine);
const dlt = dl.trim();
if (isDisplayData) {
if (dlt.startsWith("##ENDDISPLAYDATA#2d522e5a-4a6c-4aae-b20c-91c5189948d9##")) {
isDisplayData = false;
const output = REPLSession.processOutput(JSON.parse(displayData.join('')))
outputs.push(output);
await exec.appendOutput([output]);
displayData.splice(0, displayData.length);
}
else {
displayData.push(dl);
}
}
if (lines.length > 0) {
for (const [lineNumber, line] of lines.entries()) {
if (line.length > 0) {
const index = line.indexOf('##DISPLAYDATA#2d522e5a-4a6c-4aae-b20c-91c5189948d9##');
if (index === 0) {
const display_data: Record<string, string> = JSON.parse(line.substring(52));
this.currentExecution!.appendOutput([REPLSession.processOutput(display_data)]);
}
else {
this.currentExecution!.appendOutput([
new vscode.NotebookCellOutput([
vscode.NotebookCellOutputItem.stdout(filteredLines[lineNumber])
])
]);
else {
if (dlt.startsWith("##DISPLAYDATA#2d522e5a-4a6c-4aae-b20c-91c5189948d9##")) {
isDisplayData = true;
hasOutput = false;
dataLines.splice(0, dataLines.length);
}
else {
dataLines.push(dataLine);
if (REPLSession.lineIsError(dl)) {
errors.push(dataLine);
}
if (!hasOutput) {
const dlts = stripAnsi(dataLines.join('')).trim();
if (dlts.length > 0) {
hasOutput = true;
const output = new vscode.NotebookCellOutput([
vscode.NotebookCellOutputItem.stdout(dataLines.join(''))
])
outputs.push(output);
await exec.appendOutput([output]);
}
}
else {
exec.replaceOutputItems([
vscode.NotebookCellOutputItem.stdout(dataLines.join(''))
], outputs.slice(-1)[0]);
}
}
}
if (finished) {
const isOk = errors.length === 0;
this.proc.stdout!.removeListener("data", dataHandler);
this.currentExecution!.end(isOk);
resolver(isOk);
}
}
this.proc.stdout!.addListener("data", dataHandler);
let code = exec.cell.document.getText().replaceAll('\r\n', '\n').trim();
code += `\n"${executionId}"\n`;
this.proc.stdin!.write(new TextEncoder().encode(code));
return result;
});
const isOk = errors.length === 0;
exec.end(isOk);
return isOk;
}


constructor(context: vscode.ExtensionContext, onError: () => void, doc: vscode.NotebookDocument, outputChannel: vscode.OutputChannel) {
this.context = context;
this.currentDocument = doc;
Expand All @@ -141,9 +152,7 @@ export class REPLSession implements ISession {
}

public async start() {
const { lines, errors } = await this.runCode("'Welcome to Deno repl kernel'");
console.log(lines);
console.log(errors);
await this.runCode("console.log('Welcome to Deno repl kernel');");
this.started = true;
}

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "Visual Studio Code plugin for Deno lsp support on notebooks (+ NBTS serializer + DenoNBTS kernel)",
"author": "redking00 & Deno Land Inc.",
"license": "MIT",
"version": "1.0.14",
"version": "1.0.15",
"icon": "deno.png",
"galleryBanner": {
"color": "#3B3738",
Expand Down

0 comments on commit 203b156

Please sign in to comment.