[extension] Improve code running

This commit is contained in:
orosmatthew 2023-10-16 15:32:26 -04:00
parent 3d81cf91a6
commit dfa16b714a
4 changed files with 154 additions and 116 deletions

View File

@ -70,8 +70,9 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
}) })
}); });
const resData = await res.json(); const resData = await res.json();
if (resData.success !== true) { if (res.status !== 200 || resData.success !== true) {
throw new Error(resData.error.message); vscode.window.showErrorMessage('BWContest: Invalid Login');
return;
} }
const sessionToken = resData.token; const sessionToken = resData.token;
this.context.globalState.update('token', sessionToken); this.context.globalState.update('token', sessionToken);

View File

@ -1,6 +1,10 @@
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { getNonce } from './getNonce'; import { getNonce } from './getNonce';
import urlJoin from 'url-join'; import urlJoin from 'url-join';
import { extensionSettings } from './extension';
import { runJava } from './run/java';
import { join } from 'path';
import { TeamData } from './SidebarProvider';
export type ProblemData = { export type ProblemData = {
id: number; id: number;
@ -10,8 +14,21 @@ export type ProblemData = {
sampleOutput: string; sampleOutput: string;
}[]; }[];
export type MessageType = { msg: 'onRequestProblemData' }; export type MessageType =
export type WebviewMessageType = { msg: 'onProblemData'; data: ProblemData }; | { msg: 'onRequestProblemData' }
| { msg: 'onRun'; data: { problemId: number; input: string } }
| { msg: 'onKill' };
export type WebviewMessageType =
| { msg: 'onProblemData'; data: ProblemData }
| { msg: 'onRunning' }
| { msg: 'onRunningDone' }
| { msg: 'onRunningOutput'; data: string };
type RunningProgram = {
problemId: number;
outputBuffer: string[];
kill: () => void;
};
/** /**
* Singleton class for problem panel * Singleton class for problem panel
@ -19,8 +36,8 @@ export type WebviewMessageType = { msg: 'onProblemData'; data: ProblemData };
export class BWPanel { export class BWPanel {
public static currentPanel: BWPanel | undefined; public static currentPanel: BWPanel | undefined;
private running: boolean = false; private runningProgram: RunningProgram | undefined;
// private kill: () => void | null = null; private problemData: ProblemData | undefined;
private constructor( private constructor(
private readonly context: vscode.ExtensionContext, private readonly context: vscode.ExtensionContext,
@ -29,6 +46,7 @@ export class BWPanel {
private readonly webUrl: string private readonly webUrl: string
) { ) {
this.update(); this.update();
panel.onDidDispose(() => this.dispose());
} }
public static show(context: vscode.ExtensionContext, webUrl: string) { public static show(context: vscode.ExtensionContext, webUrl: string) {
@ -37,7 +55,7 @@ export class BWPanel {
: undefined; : undefined;
// Show panel if exists // Show panel if exists
if (BWPanel.currentPanel) { if (BWPanel.currentPanel !== undefined) {
BWPanel.currentPanel.panel.reveal(column); BWPanel.currentPanel.panel.reveal(column);
return; return;
} }
@ -66,7 +84,6 @@ export class BWPanel {
} }
public dispose() { public dispose() {
this.panel.dispose();
BWPanel.currentPanel = undefined; BWPanel.currentPanel = undefined;
} }
@ -80,10 +97,13 @@ export class BWPanel {
this.panel.webview.html = this._getHtmlForWebview(webview); this.panel.webview.html = this._getHtmlForWebview(webview);
webview.onDidReceiveMessage(async (m: MessageType) => { webview.onDidReceiveMessage(async (m: MessageType) => {
switch (m.msg) { switch (m.msg) {
// case 'onKill': { case 'onKill': {
// if (!this.running || !this.kill) { if (this.runningProgram !== undefined) {
// break; this.runningProgram.kill();
// } return;
}
break;
}
// this.kill(); // this.kill();
// } // }
// case 'onSubmit': { // case 'onSubmit': {
@ -113,66 +133,73 @@ export class BWPanel {
// vscode.window.showInformationMessage('Submitted!'); // vscode.window.showInformationMessage('Submitted!');
// break; // break;
// } // }
// case 'onRun': { case 'onRun': {
// if (this.running === true) { const teamData: TeamData | undefined = this.context.globalState.get('teamData');
// vscode.window.showErrorMessage('Already running'); if (teamData === undefined) {
// break; return;
// } }
// await vscode.workspace.saveAll(); if (this.problemData === undefined) {
// if (!data.value) { return;
// break; }
// } if (this.runningProgram !== undefined) {
// const repoDir = extensionSettings().repoClonePath; vscode.window.showErrorMessage('Already Running');
// this.running = true; return;
// const process = await runJava( }
// join( const problem = this.problemData.find((p) => (p.id = m.data.problemId));
// repoDir, if (problem === undefined) {
// 'BWContest', return;
// data.value.contestId.toString(), }
// data.value.teamId.toString(), await vscode.workspace.saveAll();
// data.value.problemPascalName.toString() const repoDir = extensionSettings().repoClonePath;
// ), const outputBuffer: string[] = [];
// join( this.webviewPostMessage({ msg: 'onRunning' });
// repoDir, this.webviewPostMessage({ msg: 'onRunningOutput', data: '[Compiling...]' });
// 'BWContest',
// data.value.contestId.toString(), const killFunc = await runJava(
// data.value.teamId.toString(), join(
// data.value.problemPascalName.toString(), repoDir,
// `${data.value.problemPascalName}.java` 'BWContest',
// ), teamData.contestId.toString(),
// data.value.problemPascalName, teamData.teamId.toString(),
// data.value.input problem.pascalName
// ); ),
// if (!process) { join(
// this.panel.webview.postMessage({ repoDir,
// type: 'onOutput', 'BWContest',
// value: '[An error occurred while running]' teamData.contestId.toString(),
// }); teamData.teamId.toString(),
// break; problem.pascalName,
// } `${problem.pascalName}.java`
// process.output ),
// .then((output) => { problem.pascalName,
// this.panel.webview.postMessage({ type: 'onOutput', value: output }); m.data.input,
// this.running = false; (data: string) => {
// this.kill = null; outputBuffer.push(data);
// }) this.webviewPostMessage({ msg: 'onRunningOutput', data: outputBuffer.join('') });
// .catch(() => { },
// this.panel.webview.postMessage({ () => {
// type: 'onOutput', this.runningProgram = undefined;
// value: '[An error occurred while running]' this.webviewPostMessage({ msg: 'onRunningDone' });
// }); }
// this.running = false; );
// this.kill = null; if (killFunc !== undefined) {
// }); this.runningProgram = {
// this.kill = process.kill; problemId: m.data.problemId,
// break; outputBuffer: outputBuffer,
// } kill: killFunc
};
} else {
this.webviewPostMessage({ msg: 'onRunningDone' });
}
break;
}
case 'onRequestProblemData': { case 'onRequestProblemData': {
const token: string | undefined = this.context.globalState.get('token'); const token: string | undefined = this.context.globalState.get('token');
if (token !== undefined) { if (token !== undefined) {
const res = await fetch(urlJoin(this.webUrl, `/api/contest/${token}`)); const res = await fetch(urlJoin(this.webUrl, `/api/contest/${token}`));
const data = await res.json(); const data = await res.json();
if (data.success === true) { if (data.success === true) {
this.problemData = data.problems;
this.webviewPostMessage({ this.webviewPostMessage({
msg: 'onProblemData', msg: 'onProblemData',
data: data.problems data: data.problems

View File

@ -13,62 +13,72 @@ export async function runJava(
srcDir: string, srcDir: string,
mainFile: string, mainFile: string,
mainClass: string, mainClass: string,
input: string input: string,
): Promise<{ output: Promise<string>; kill: () => void | null }> { outputCallback: (data: string) => void,
doneCallback: () => void
): Promise<(() => void) | undefined> {
const javaPath = extensionSettings().javaPath; const javaPath = extensionSettings().javaPath;
if (javaPath == '') { if (javaPath == '') {
throw error('Java path not set'); throw error('Java path not set');
} }
const tempDir = os.tmpdir(); const tempDir = os.tmpdir();
const buildDir = join(tempDir, 'bwcontest_java'); const buildDir = join(tempDir, 'bwcontest_java');
if (fs.existsSync(buildDir)) { if (await fs.exists(buildDir)) {
fs.removeSync(buildDir); await fs.remove(buildDir);
} }
fs.mkdirSync(buildDir); await fs.mkdir(buildDir);
const compileCommand = `${join(javaPath, 'javac')} -cp ${srcDir} ${mainFile} -d ${buildDir}`; const compileCommand = `${join(javaPath, 'javac')} -cp ${srcDir} ${mainFile} -d ${buildDir}`;
await execPromise(compileCommand); try {
await execPromise(compileCommand);
} catch (error) {
outputCallback('[Compile Error]\n\n');
outputCallback(String(error));
return;
}
const runCommand = `${join(javaPath, 'java')} -cp "${buildDir}" ${mainClass}`; const runCommand = `${join(javaPath, 'java')} -cp "${buildDir}" ${mainClass}`;
const child = spawn(runCommand, { shell: true }); const child = spawn(runCommand, { shell: true });
let outputBuffer = '';
return {
output: new Promise((resolve) => {
child.stdout.setEncoding('utf8');
child.stdout.on('data', (data) => {
outputBuffer += data.toString();
});
child.stderr.setEncoding('utf8');
child.stderr.on('data', (data) => {
outputBuffer += data.toString();
});
child.stdin.write(input);
child.stdin.end();
let resolved = false; child.stdout.setEncoding('utf8');
child.stdout.on('data', (data) => {
outputCallback(data.toString());
});
child.stderr.setEncoding('utf8');
child.stderr.on('data', (data) => {
outputCallback(data.toString());
});
child.stdin.write(input);
child.stdin.end();
child.on('close', () => { let done = false;
if (!resolved) {
resolved = true;
resolve(outputBuffer);
}
});
setTimeout(() => { child.on('close', () => {
if (!resolved) { if (done === false) {
console.log('30 seconds reached, killing process'); done = true;
resolved = true; doneCallback();
child.kill('SIGKILL'); }
resolve(outputBuffer + '\n[Timeout after 30 seconds]'); });
}
}, 30000); setTimeout(() => {
}), if (done === false) {
kill: () => { console.log('\n[30 seconds reached, killing process...]');
done = true;
if (child.pid) { if (child.pid) {
outputBuffer += '\n[Manually stopped]';
kill(child.pid); kill(child.pid);
} }
outputCallback('\n[Timeout after 30 seconds]');
doneCallback();
}
}, 30000);
return () => {
if (child.pid) {
done = true;
kill(child.pid);
outputCallback('\n[Manually stopped]');
doneCallback();
} }
}; };
} }

View File

@ -29,15 +29,12 @@
} }
function onRun() { function onRun() {
// if (problemData !== undefined && running === false) { if (problemData !== undefined) {
// postMessage({ postMessage({
// type: 'requestRun', msg: 'onRun',
// value: { data: { input: sampleInputValue, problemId: problemData[activeProblemIndex].id }
// problemId: problemData[activeProblemIndex].id, });
// input: sampleInputValue }
// }
// });
// }
} }
function updateTextBoxes() { function updateTextBoxes() {
@ -68,7 +65,7 @@
} }
function onKill() { function onKill() {
// postMessage({ type: 'onKill' }); postMessage({ msg: 'onKill' });
} }
onMount(() => { onMount(() => {
@ -77,12 +74,15 @@
window.addEventListener('message', async (event) => { window.addEventListener('message', async (event) => {
const m = (event as MessageEvent).data as WebviewMessageType; const m = (event as MessageEvent).data as WebviewMessageType;
// if (message.msg === 'onOutput') {
// outputValue = message.value;
// running = false;
if (m.msg === 'onProblemData') { if (m.msg === 'onProblemData') {
problemData = m.data; problemData = m.data;
updateTextBoxes(); updateTextBoxes();
} else if (m.msg === 'onRunning') {
running = true;
} else if (m.msg === 'onRunningDone') {
running = false;
} else if (m.msg === 'onRunningOutput') {
outputValue = m.data;
} }
}); });
</script> </script>