[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();
if (resData.success !== true) {
throw new Error(resData.error.message);
if (res.status !== 200 || resData.success !== true) {
vscode.window.showErrorMessage('BWContest: Invalid Login');
return;
}
const sessionToken = resData.token;
this.context.globalState.update('token', sessionToken);

View File

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

View File

@ -13,62 +13,72 @@ export async function runJava(
srcDir: string,
mainFile: string,
mainClass: string,
input: string
): Promise<{ output: Promise<string>; kill: () => void | null }> {
input: string,
outputCallback: (data: string) => void,
doneCallback: () => void
): Promise<(() => void) | undefined> {
const javaPath = extensionSettings().javaPath;
if (javaPath == '') {
throw error('Java path not set');
}
const tempDir = os.tmpdir();
const buildDir = join(tempDir, 'bwcontest_java');
if (fs.existsSync(buildDir)) {
fs.removeSync(buildDir);
if (await fs.exists(buildDir)) {
await fs.remove(buildDir);
}
fs.mkdirSync(buildDir);
await fs.mkdir(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 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', () => {
if (!resolved) {
resolved = true;
resolve(outputBuffer);
}
});
let done = false;
setTimeout(() => {
if (!resolved) {
console.log('30 seconds reached, killing process');
resolved = true;
child.kill('SIGKILL');
resolve(outputBuffer + '\n[Timeout after 30 seconds]');
}
}, 30000);
}),
kill: () => {
child.on('close', () => {
if (done === false) {
done = true;
doneCallback();
}
});
setTimeout(() => {
if (done === false) {
console.log('\n[30 seconds reached, killing process...]');
done = true;
if (child.pid) {
outputBuffer += '\n[Manually stopped]';
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() {
// if (problemData !== undefined && running === false) {
// postMessage({
// type: 'requestRun',
// value: {
// problemId: problemData[activeProblemIndex].id,
// input: sampleInputValue
// }
// });
// }
if (problemData !== undefined) {
postMessage({
msg: 'onRun',
data: { input: sampleInputValue, problemId: problemData[activeProblemIndex].id }
});
}
}
function updateTextBoxes() {
@ -68,7 +65,7 @@
}
function onKill() {
// postMessage({ type: 'onKill' });
postMessage({ msg: 'onKill' });
}
onMount(() => {
@ -77,12 +74,15 @@
window.addEventListener('message', async (event) => {
const m = (event as MessageEvent).data as WebviewMessageType;
// if (message.msg === 'onOutput') {
// outputValue = message.value;
// running = false;
if (m.msg === 'onProblemData') {
problemData = m.data;
updateTextBoxes();
} else if (m.msg === 'onRunning') {
running = true;
} else if (m.msg === 'onRunningDone') {
running = false;
} else if (m.msg === 'onRunningOutput') {
outputValue = m.data;
}
});
</script>