diff --git a/extension/bwcontest/package.json b/extension/bwcontest/package.json index 74a1419..cfa80d1 100644 --- a/extension/bwcontest/package.json +++ b/extension/bwcontest/package.json @@ -24,6 +24,11 @@ "type": "string", "default": "", "description": "The path where the repos are cloned to" + }, + "BWContest.javaPath": { + "type": "string", + "default": "", + "description": "Path of java bin folder" } } }, diff --git a/extension/bwcontest/src/extension.ts b/extension/bwcontest/src/extension.ts index 787570b..fc1fd1a 100644 --- a/extension/bwcontest/src/extension.ts +++ b/extension/bwcontest/src/extension.ts @@ -4,9 +4,14 @@ import * as child_process from 'child_process'; import * as fs from 'fs-extra'; import { BWPanel } from './problemPanel'; -interface BWContestSettings { +export interface BWContestSettings { repoBaseUrl: string; repoClonePath: string; + javaPath: string; +} + +export function extensionSettings(): BWContestSettings { + return vscode.workspace.getConfiguration().get('BWContest')!; } function closeAllWorkspaces() { diff --git a/extension/bwcontest/src/problemPanel.ts b/extension/bwcontest/src/problemPanel.ts index 8c2d13b..ac4dffe 100644 --- a/extension/bwcontest/src/problemPanel.ts +++ b/extension/bwcontest/src/problemPanel.ts @@ -1,5 +1,8 @@ import * as vscode from 'vscode'; import { getNonce } from './getNonce'; +import { runJava } from './run/java'; +import { extensionSettings } from './extension'; +import { join } from 'path'; export class BWPanel { /** @@ -36,6 +39,8 @@ export class BWPanel { // Enable javascript in the webview enableScripts: true, + retainContextWhenHidden: true, + // And restrict the webview to only loading content from our extension's `media` directory. localResourceRoots: [ vscode.Uri.joinPath(extensionUri, 'media'), @@ -101,6 +106,33 @@ export class BWPanel { this._panel.webview.html = this._getHtmlForWebview(webview); webview.onDidReceiveMessage(async (data) => { switch (data.type) { + case 'onRun': { + if (!data.value) { + return; + } + const repoDir = extensionSettings().repoClonePath; + const output = 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 + ); + this._panel.webview.postMessage({ type: 'onOutput', value: output }); + break; + } case 'onStartup': { const token: string | undefined = BWPanel._context?.globalState.get('token'); diff --git a/extension/bwcontest/src/run/java.ts b/extension/bwcontest/src/run/java.ts new file mode 100644 index 0000000..b03fc57 --- /dev/null +++ b/extension/bwcontest/src/run/java.ts @@ -0,0 +1,51 @@ +import * as fs from 'fs-extra'; +import { join } from 'path'; +import os = require('os'); +import { exec, spawn } from 'child_process'; +import { extensionSettings } from '../extension'; +import { error } from 'console'; +import util = require('node:util'); + +const execPromise = util.promisify(exec); + +export async function runJava( + srcDir: string, + mainFile: string, + mainClass: string, + input: string +): Promise { + 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); + } + fs.mkdirSync(buildDir); + + const compileCommand = `${join(javaPath, 'javac')} -cp ${srcDir} ${mainFile} -d ${buildDir}`; + await execPromise(compileCommand); + + const runCommand = `${join(javaPath, 'java')} -cp "${buildDir}" ${mainClass}`; + + return new Promise((resolve) => { + let outputBuffer = ''; + const child = spawn(runCommand, { shell: true }); + 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(); + + child.on('close', () => { + resolve(outputBuffer); + }); + }); +} diff --git a/extension/bwcontest/webviews/components/ProblemPanel.svelte b/extension/bwcontest/webviews/components/ProblemPanel.svelte index 5cf45eb..4c15cd2 100644 --- a/extension/bwcontest/webviews/components/ProblemPanel.svelte +++ b/extension/bwcontest/webviews/components/ProblemPanel.svelte @@ -8,15 +8,21 @@ type ProblemData = { id: number, name: string, + pascalName: string, sampleInput: string, sampleOutput: string }[]; + let savedInputs: Map = new Map(); + let activeProblem: ProblemData[0]; let sessionToken: string | undefined; let problemData: ProblemData | undefined; let sampleInputText: HTMLTextAreaElement; + let outputText: HTMLTextAreaElement; + + let running = false; $: if (problemData && problemData.length !== 0) { let first = problemData.at(0); @@ -29,12 +35,34 @@ sampleInputText.value = activeProblem.sampleInput; } + let contestId: number | undefined; + let teamId: number | undefined; + + function onRun() { + if (!running && contestId && teamId) { + postMessage({type: 'onRun', value: {problemPascalName: activeProblem.pascalName, contestId: contestId, teamId: teamId, input: sampleInputText.value}}); + running = true; + } + } + + function updateTextBoxes() { + if (savedInputs.has(activeProblem.id)) { + sampleInputText.value = savedInputs.get(activeProblem.id)!.input; + outputText.value = savedInputs.get(activeProblem.id)!.output; + } else { + sampleInputText.value = activeProblem.sampleInput; + outputText.value = "[Run to get output]"; + } + } + async function fetchProblemData() { if (sessionToken) { const res = await fetch(`http://localhost:5173/api/contest/${sessionToken}`); const data = await res.json(); if (data.success === true) { problemData = data.problems as ProblemData; + contestId = data.contestId; + teamId = data.teamId; } } } @@ -45,7 +73,11 @@ if (message.value !== "") { sessionToken = message.value; await fetchProblemData(); + updateTextBoxes(); } + } else if (message.type === 'onOutput') { + outputText.value = message.value; + running = false; } }) @@ -61,7 +93,11 @@
{#each problemData as problem} {/each}
@@ -72,12 +108,18 @@

Sample Input (You can edit this!)

- + +
+

Output

+ {#if running} + + {/if} +
+