Skip to content

Commit c883788

Browse files
authored
Persistent sessions (#118)
* Initial WIP implementation of the panel * WIP: MVP Judge background sessions * At least got backing to background working * Fix regression of non-compiled languages being stuck in compiling * Add command and context item to stop a file's running tests * Add command to stop all running tests across all files * Remove unused import * Make debugging resistant to running while already ran * Run after attach listeners in interactive tests * Also include current file running testcases for judge * Implement persistency for stress tester * Debounce save * Remove console log * Documented persistent changes in changelog * Also save on dispose for stress tester * Fix race condition * Dynamically updated status item text * Remove background from title * Properly handle synchronization of judge tests on startup
1 parent 64b0548 commit c883788

File tree

14 files changed

+1407
-877
lines changed

14 files changed

+1407
-877
lines changed

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
### Added
44

5+
- Persistent sessions for running testcases and stress tester. Whenever the active editor changes, the previous session is saved and restored when the file is opened again
56
- Ctrl+Enter hotkey to save currently edited textarea
67
- Ctrl+Enter to append newline instead of sending current online input
78
- Automatically show notification to check changelog when extension is updated
@@ -10,9 +11,9 @@
1011

1112
### Changed
1213

13-
- Use native addons to run solutions and enforce limits instead of using child_process. This bypasses the event loop and allows for more accurate limits as well as accurate metrics. Using native addons also means we effectively restrict this extension to only run on Windows, Linux, and macOS, which are the platforms that VSCode supports
14+
- Use native addons to run solutions and enforce limits instead of using child_process. This bypasses the event loop and allows for more accurate limits as well as accurate metrics. Using native addons also means we effectively restrict this extension to only run on Windows, Linux, and macOS. The web platform is excluded.
1415
- Use total CPU time to enforce time limit and a 2x multipler to enforce the wall time
15-
- The extension excludes the web platform support explicitly and will not provide a "universal" VSIX. It would have failed implicity in the past, but now this restriction is enforced to platform specific VSIX.
16+
- Changes are debounced on judge prevent rapid IO bottlenecks
1617
- Trim off trailing whitespaces when requesting full data
1718
- Don't save ongoing running statuses to avoid blocking interacting with testcase on malfunction
1819
- Removed unused save all functionality

package.json

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,38 @@
7575
"command": "fastolympiccoding.toggleJudgeSettings",
7676
"when": "false"
7777
}
78+
],
79+
"view/item/context": [
80+
{
81+
"command": "fastolympiccoding.listenForCompetitiveCompanion",
82+
"group": "inline",
83+
"when": "view == fastolympiccoding.panel && viewItem == companion-stopped"
84+
},
85+
{
86+
"command": "fastolympiccoding.stopCompetitiveCompanion",
87+
"group": "inline",
88+
"when": "view == fastolympiccoding.panel && viewItem == companion-listening"
89+
},
90+
{
91+
"command": "fastolympiccoding.stopBackgroundTests",
92+
"group": "inline",
93+
"when": "view == fastolympiccoding.panel && viewItem == judge-background-file"
94+
},
95+
{
96+
"command": "fastolympiccoding.stopStressSession",
97+
"group": "inline",
98+
"when": "view == fastolympiccoding.panel && viewItem == stress-background-file"
99+
},
100+
{
101+
"command": "fastolympiccoding.stopAllBackgroundTests",
102+
"group": "inline",
103+
"when": "view == fastolympiccoding.panel && viewItem == judge-background-group"
104+
},
105+
{
106+
"command": "fastolympiccoding.stopAllStressSessions",
107+
"group": "inline",
108+
"when": "view == fastolympiccoding.panel && viewItem == stress-background-group"
109+
}
78110
]
79111
},
80112
"viewsContainers": {
@@ -84,6 +116,13 @@
84116
"title": "Fast Olympic Coding",
85117
"icon": "$(zap)"
86118
}
119+
],
120+
"panel": [
121+
{
122+
"id": "fastolympiccodingpanel",
123+
"title": "Fast Olympic Coding",
124+
"icon": "$(zap)"
125+
}
87126
]
88127
},
89128
"views": {
@@ -100,6 +139,13 @@
100139
"type": "webview",
101140
"name": "Stress"
102141
}
142+
],
143+
"fastolympiccodingpanel": [
144+
{
145+
"id": "fastolympiccoding.panel",
146+
"name": "Status",
147+
"icon": "$(zap)"
148+
}
103149
]
104150
},
105151
"configuration": [
@@ -295,12 +341,14 @@
295341
{
296342
"command": "fastolympiccoding.listenForCompetitiveCompanion",
297343
"title": "Listen for Competitive Companion",
298-
"category": "Fast Olympic Coding"
344+
"category": "Fast Olympic Coding",
345+
"icon": "$(broadcast)"
299346
},
300347
{
301348
"command": "fastolympiccoding.stopCompetitiveCompanion",
302349
"title": "Stop Competitive Companion",
303-
"category": "Fast Olympic Coding"
350+
"category": "Fast Olympic Coding",
351+
"icon": "$(circle-slash)"
304352
},
305353
{
306354
"command": "fastolympiccoding.toggleJudgeSettings",
@@ -318,6 +366,30 @@
318366
"command": "fastolympiccoding.showChangelog",
319367
"title": "Show Changelog",
320368
"category": "Fast Olympic Coding"
369+
},
370+
{
371+
"command": "fastolympiccoding.stopBackgroundTests",
372+
"title": "Stop",
373+
"category": "Fast Olympic Coding",
374+
"icon": "$(debug-stop)"
375+
},
376+
{
377+
"command": "fastolympiccoding.stopStressSession",
378+
"title": "Stop Stress Test",
379+
"category": "Fast Olympic Coding",
380+
"icon": "$(debug-stop)"
381+
},
382+
{
383+
"command": "fastolympiccoding.stopAllBackgroundTests",
384+
"title": "Stop All Running Tests",
385+
"category": "Fast Olympic Coding",
386+
"icon": "$(circle-slash)"
387+
},
388+
{
389+
"command": "fastolympiccoding.stopAllStressSessions",
390+
"title": "Stop All Stress Sessions",
391+
"category": "Fast Olympic Coding",
392+
"icon": "$(circle-slash)"
321393
}
322394
],
323395
"keybindings": [

src/extension/competitiveCompanion.ts

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ class ProblemQueue {
3838

3939
// Module state
4040
let server: http.Server | undefined;
41-
let statusBarItem: vscode.StatusBarItem | undefined;
4241

4342
let prevSelection: string | undefined;
4443
let prevBatchId: string | undefined;
@@ -226,23 +225,6 @@ function createRequestHandler(judge: JudgeViewProvider): http.RequestListener {
226225
};
227226
}
228227

229-
/**
230-
* Creates the status bar item for Competitive Companion.
231-
*/
232-
export function createStatusBarItem(context: vscode.ExtensionContext): vscode.StatusBarItem {
233-
statusBarItem = vscode.window.createStatusBarItem(
234-
"fastolympiccoding.listeningForCompetitiveCompanion",
235-
vscode.StatusBarAlignment.Left
236-
);
237-
statusBarItem.name = "Competitive Companion Indicator";
238-
statusBarItem.text = "$(zap)";
239-
statusBarItem.tooltip = "Listening For Competitive Companion";
240-
statusBarItem.hide();
241-
context.subscriptions.push(statusBarItem);
242-
243-
return statusBarItem;
244-
}
245-
246228
/**
247229
* Starts listening for Competitive Companion connections.
248230
*/
@@ -259,7 +241,7 @@ export function createListener(judgeViewProvider: JudgeViewProvider): void {
259241
const port = config.get<number>("port")!;
260242
const logger = getLogger("competitive-companion");
261243
logger.info(`Listener started on port ${port}`);
262-
statusBarItem?.show();
244+
_onDidChangeListening.fire(true);
263245
});
264246
server.once("error", (error) => {
265247
const logger = getLogger("competitive-companion");
@@ -270,7 +252,7 @@ export function createListener(judgeViewProvider: JudgeViewProvider): void {
270252
});
271253
server.once("close", () => {
272254
server = undefined;
273-
statusBarItem?.hide();
255+
_onDidChangeListening.fire(false);
274256
});
275257

276258
const config = vscode.workspace.getConfiguration("fastolympiccoding");
@@ -282,6 +264,15 @@ export function createListener(judgeViewProvider: JudgeViewProvider): void {
282264
* Stops listening for Competitive Companion connections.
283265
*/
284266
export function stopCompetitiveCompanion(): void {
285-
server?.close();
286-
server = undefined;
267+
if (server) {
268+
server.close();
269+
server = undefined;
270+
}
271+
}
272+
273+
export function isListening(): boolean {
274+
return server !== undefined;
287275
}
276+
277+
const _onDidChangeListening = new vscode.EventEmitter<boolean>();
278+
export const onDidChangeListening = _onDidChangeListening.event;

src/extension/index.ts

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,7 @@ import * as path from "node:path";
33
import * as vscode from "vscode";
44

55
import { compile, clearCompileCache } from "./utils/runtime";
6-
import {
7-
createStatusBarItem,
8-
createListener,
9-
stopCompetitiveCompanion,
10-
} from "./competitiveCompanion";
6+
import { createListener, stopCompetitiveCompanion } from "./competitiveCompanion";
117
import { registerRunSettingsCommands } from "./runSettingsCommands";
128
import {
139
initializeRunSettingsWatcher,
@@ -17,10 +13,13 @@ import {
1713
import { initLogging } from "./utils/logging";
1814
import JudgeViewProvider from "./providers/JudgeViewProvider";
1915
import StressViewProvider from "./providers/StressViewProvider";
16+
import PanelViewProvider from "./providers/PanelViewProvider";
2017
import { showChangelog } from "./changelog";
18+
import { createStatusBarItem } from "./statusBar";
2119

2220
let judgeViewProvider: JudgeViewProvider;
2321
let stressViewProvider: StressViewProvider;
22+
let panelViewProvider: PanelViewProvider;
2423

2524
type Dependencies = Record<string, string[]>;
2625

@@ -76,6 +75,15 @@ function registerViewProviders(context: vscode.ExtensionContext): void {
7675
context.subscriptions.push(
7776
vscode.window.registerWebviewViewProvider(stressViewProvider.getViewId(), stressViewProvider)
7877
);
78+
79+
// Panel tree view in panel area (bottom)
80+
panelViewProvider = new PanelViewProvider(context, judgeViewProvider, stressViewProvider);
81+
context.subscriptions.push(
82+
vscode.window.createTreeView("fastolympiccoding.panel", {
83+
treeDataProvider: panelViewProvider,
84+
showCollapseAll: false,
85+
})
86+
);
7987
}
8088

8189
function registerDocumentContentProviders(context: vscode.ExtensionContext): void {
@@ -246,6 +254,46 @@ function registerCommands(context: vscode.ExtensionContext): void {
246254
context.subscriptions.push(
247255
vscode.commands.registerCommand("fastolympiccoding.showChangelog", () => showChangelog(context))
248256
);
257+
258+
context.subscriptions.push(
259+
vscode.commands.registerCommand("fastolympiccoding.showPanel", () => {
260+
vscode.commands.executeCommand("fastolympiccoding.panel.focus");
261+
})
262+
);
263+
264+
context.subscriptions.push(
265+
vscode.commands.registerCommand(
266+
"fastolympiccoding.stopBackgroundTests",
267+
(item: { filePath?: string }) => {
268+
if (item?.filePath) {
269+
void judgeViewProvider.stopBackgroundTasksForFile(item.filePath);
270+
}
271+
}
272+
)
273+
);
274+
275+
context.subscriptions.push(
276+
vscode.commands.registerCommand(
277+
"fastolympiccoding.stopStressSession",
278+
(item: { filePath?: string }) => {
279+
if (item?.filePath) {
280+
stressViewProvider.stopStressSession(item.filePath);
281+
}
282+
}
283+
)
284+
);
285+
286+
context.subscriptions.push(
287+
vscode.commands.registerCommand("fastolympiccoding.stopAllStressSessions", () => {
288+
stressViewProvider.stopAll();
289+
})
290+
);
291+
292+
context.subscriptions.push(
293+
vscode.commands.registerCommand("fastolympiccoding.stopAllBackgroundTests", () => {
294+
void judgeViewProvider.stopAllBackgroundTasks();
295+
})
296+
);
249297
}
250298

251299
export function activate(context: vscode.ExtensionContext): void {

0 commit comments

Comments
 (0)