[extension] Auto and manual stop process
This commit is contained in:
parent
6261ee0bde
commit
9540e6eb48
2
extension/bwcontest/package-lock.json
generated
2
extension/bwcontest/package-lock.json
generated
@ -10,6 +10,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.4.0",
|
"axios": "^1.4.0",
|
||||||
"fs-extra": "^11.1.1",
|
"fs-extra": "^11.1.1",
|
||||||
|
"tree-kill": "^1.2.2",
|
||||||
"vsce": "^2.15.0"
|
"vsce": "^2.15.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -5300,7 +5301,6 @@
|
|||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
|
||||||
"integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
|
"integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
|
||||||
"dev": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"tree-kill": "cli.js"
|
"tree-kill": "cli.js"
|
||||||
}
|
}
|
||||||
|
@ -103,6 +103,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.4.0",
|
"axios": "^1.4.0",
|
||||||
"fs-extra": "^11.1.1",
|
"fs-extra": "^11.1.1",
|
||||||
|
"tree-kill": "^1.2.2",
|
||||||
"vsce": "^2.15.0"
|
"vsce": "^2.15.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,8 @@ export class BWPanel {
|
|||||||
private readonly _extensionUri: vscode.Uri;
|
private readonly _extensionUri: vscode.Uri;
|
||||||
private _disposables: vscode.Disposable[] = [];
|
private _disposables: vscode.Disposable[] = [];
|
||||||
private static _context?: vscode.ExtensionContext;
|
private static _context?: vscode.ExtensionContext;
|
||||||
|
private static _running: boolean;
|
||||||
|
private static _kill: Function | null;
|
||||||
|
|
||||||
public static createOrShow(context: vscode.ExtensionContext) {
|
public static createOrShow(context: vscode.ExtensionContext) {
|
||||||
this._context = context;
|
this._context = context;
|
||||||
@ -85,6 +87,12 @@ export class BWPanel {
|
|||||||
this._panel.webview.html = this._getHtmlForWebview(webview);
|
this._panel.webview.html = this._getHtmlForWebview(webview);
|
||||||
webview.onDidReceiveMessage(async (data) => {
|
webview.onDidReceiveMessage(async (data) => {
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
|
case 'onKill': {
|
||||||
|
if (!BWPanel._running || !BWPanel._kill) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
BWPanel._kill();
|
||||||
|
}
|
||||||
case 'onSubmit': {
|
case 'onSubmit': {
|
||||||
await vscode.workspace.saveAll();
|
await vscode.workspace.saveAll();
|
||||||
if (!data.value) {
|
if (!data.value) {
|
||||||
@ -113,12 +121,17 @@ export class BWPanel {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'onRun': {
|
case 'onRun': {
|
||||||
|
if (BWPanel._running === true) {
|
||||||
|
vscode.window.showErrorMessage('Already running');
|
||||||
|
break;
|
||||||
|
}
|
||||||
await vscode.workspace.saveAll();
|
await vscode.workspace.saveAll();
|
||||||
if (!data.value) {
|
if (!data.value) {
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
const repoDir = extensionSettings().repoClonePath;
|
const repoDir = extensionSettings().repoClonePath;
|
||||||
runJava(
|
BWPanel._running = true;
|
||||||
|
const process = await runJava(
|
||||||
join(
|
join(
|
||||||
repoDir,
|
repoDir,
|
||||||
'BWContest',
|
'BWContest',
|
||||||
@ -136,16 +149,29 @@ export class BWPanel {
|
|||||||
),
|
),
|
||||||
data.value.problemPascalName,
|
data.value.problemPascalName,
|
||||||
data.value.input
|
data.value.input
|
||||||
)
|
);
|
||||||
|
if (!process) {
|
||||||
|
this._panel.webview.postMessage({
|
||||||
|
type: 'onOutput',
|
||||||
|
value: '[An error occurred while running]'
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
process.output
|
||||||
.then((output) => {
|
.then((output) => {
|
||||||
this._panel.webview.postMessage({ type: 'onOutput', value: output });
|
this._panel.webview.postMessage({ type: 'onOutput', value: output });
|
||||||
|
BWPanel._running = false;
|
||||||
|
BWPanel._kill = null;
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this._panel.webview.postMessage({
|
this._panel.webview.postMessage({
|
||||||
type: 'onOutput',
|
type: 'onOutput',
|
||||||
value: '[An error occurred while running]'
|
value: '[An error occurred while running]'
|
||||||
});
|
});
|
||||||
|
BWPanel._running = false;
|
||||||
|
BWPanel._kill = null;
|
||||||
});
|
});
|
||||||
|
BWPanel._kill = process.kill;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'onStartup': {
|
case 'onStartup': {
|
||||||
|
@ -5,6 +5,7 @@ import { exec, spawn } from 'child_process';
|
|||||||
import { extensionSettings } from '../extension';
|
import { extensionSettings } from '../extension';
|
||||||
import { error } from 'console';
|
import { error } from 'console';
|
||||||
import util = require('node:util');
|
import util = require('node:util');
|
||||||
|
import kill = require('tree-kill');
|
||||||
|
|
||||||
const execPromise = util.promisify(exec);
|
const execPromise = util.promisify(exec);
|
||||||
|
|
||||||
@ -13,7 +14,7 @@ export async function runJava(
|
|||||||
mainFile: string,
|
mainFile: string,
|
||||||
mainClass: string,
|
mainClass: string,
|
||||||
input: string
|
input: string
|
||||||
): Promise<string> {
|
): Promise<{ output: Promise<string>; kill: Function | null }> {
|
||||||
const javaPath = extensionSettings().javaPath;
|
const javaPath = extensionSettings().javaPath;
|
||||||
if (javaPath == '') {
|
if (javaPath == '') {
|
||||||
throw error('Java path not set');
|
throw error('Java path not set');
|
||||||
@ -30,22 +31,44 @@ export async function runJava(
|
|||||||
|
|
||||||
const runCommand = `${join(javaPath, 'java')} -cp "${buildDir}" ${mainClass}`;
|
const runCommand = `${join(javaPath, 'java')} -cp "${buildDir}" ${mainClass}`;
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
const child = spawn(runCommand, { shell: true });
|
||||||
let outputBuffer = '';
|
let outputBuffer = '';
|
||||||
const child = spawn(runCommand, { shell: true });
|
return {
|
||||||
child.stdout.setEncoding('utf8');
|
output: new Promise((resolve) => {
|
||||||
child.stdout.on('data', (data) => {
|
child.stdout.setEncoding('utf8');
|
||||||
outputBuffer += data.toString();
|
child.stdout.on('data', (data) => {
|
||||||
});
|
outputBuffer += data.toString();
|
||||||
child.stderr.setEncoding('utf8');
|
});
|
||||||
child.stderr.on('data', (data) => {
|
child.stderr.setEncoding('utf8');
|
||||||
outputBuffer += data.toString();
|
child.stderr.on('data', (data) => {
|
||||||
});
|
outputBuffer += data.toString();
|
||||||
child.stdin.write(input);
|
});
|
||||||
child.stdin.end();
|
child.stdin.write(input);
|
||||||
|
child.stdin.end();
|
||||||
|
|
||||||
child.on('close', () => {
|
let resolved = false;
|
||||||
resolve(outputBuffer);
|
|
||||||
});
|
child.on('close', () => {
|
||||||
});
|
if (!resolved) {
|
||||||
|
resolved = true;
|
||||||
|
resolve(outputBuffer);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!resolved) {
|
||||||
|
console.log('30 seconds reached, killing process');
|
||||||
|
resolved = true;
|
||||||
|
child.kill('SIGKILL');
|
||||||
|
resolve(outputBuffer + '\n[Timeout after 30 seconds]');
|
||||||
|
}
|
||||||
|
}, 30000);
|
||||||
|
}),
|
||||||
|
kill: () => {
|
||||||
|
if (child.pid) {
|
||||||
|
outputBuffer += '\n[Manually stopped]';
|
||||||
|
kill(child.pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onKill() {
|
||||||
|
postMessage({type: 'onKill'});
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchProblemData() {
|
async function fetchProblemData() {
|
||||||
if (sessionToken) {
|
if (sessionToken) {
|
||||||
const res = await fetch(`http://localhost:5173/api/contest/${sessionToken}`);
|
const res = await fetch(`http://localhost:5173/api/contest/${sessionToken}`);
|
||||||
@ -125,7 +129,11 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<textarea bind:this={outputText} disabled />
|
<textarea bind:this={outputText} disabled />
|
||||||
<button style="margin-top:5px" on:click={onRun} type="button">Run</button>
|
{#if !running}
|
||||||
|
<button style="margin-top:5px" on:click={onRun} type="button">Run</button>
|
||||||
|
{:else}
|
||||||
|
<button style="margin-top:5px" on:click={onKill} type="button">Stop</button>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button on:click={onSubmit} type="button">Submit</button>
|
<button on:click={onSubmit} type="button">Submit</button>
|
||||||
|
Loading…
Reference in New Issue
Block a user