This commit is contained in:
2025-08-06 22:52:32 +02:00
parent fd01ccf8af
commit 272812a815
2 changed files with 162 additions and 76 deletions
+83 -55
View File
@@ -1,12 +1,12 @@
import * as vscode from 'vscode'; import * as vscode from "vscode";
import os from 'os'; import os from "os";
import path from 'path'; import path from "path";
import fs from 'fs'; import fs from "fs";
import testCode, { TestResult } from './tester'; import testCode, { TestResult } from "./tester";
import * as utils from './utils'; import * as utils from "./utils";
import { MyTreeDataProvider } from './resultViewer'; import { MyTreeDataProvider } from "./resultViewer";
let resetTimer = () => {} let resetTimer = () => {};
const speedCodePath = path.join(os.homedir(), ".speed-code/"); const speedCodePath = path.join(os.homedir(), ".speed-code/");
@@ -16,7 +16,10 @@ if (!fs.existsSync(speedCodePath)) {
export async function openAndFocusFile(filePath: string) { export async function openAndFocusFile(filePath: string) {
const document = await vscode.workspace.openTextDocument(filePath); const document = await vscode.workspace.openTextDocument(filePath);
await vscode.window.showTextDocument(document, { preview: false, preserveFocus: false }); await vscode.window.showTextDocument(document, {
preview: false,
preserveFocus: false,
});
} }
const userCode = ` const userCode = `
@@ -26,12 +29,25 @@ function isPalindrome(s) {
`; `;
export function activate(context: vscode.ExtensionContext) { export function activate(context: vscode.ExtensionContext) {
const statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 100); const statusBarItem = vscode.window.createStatusBarItem(
statusBarItem.tooltip = 'Speedrun timer'; vscode.StatusBarAlignment.Left,
100
);
statusBarItem.tooltip = "Speedrun timer";
statusBarItem.text = "No active Speedrun"; statusBarItem.text = "No active Speedrun";
statusBarItem.show(); statusBarItem.show();
let run: undefined | { path: string, elapsed: number, timer: NodeJS.Timeout, blunders: [number, TestResult[]][] }; let run:
| undefined
| {
path: string;
elapsed: number;
timer: NodeJS.Timeout;
};
const treeDataProvider = new MyTreeDataProvider();
vscode.window.registerTreeDataProvider("myCustomTab", treeDataProvider);
resetTimer = () => { resetTimer = () => {
if (run) { if (run) {
@@ -43,57 +59,69 @@ export function activate(context: vscode.ExtensionContext) {
resetTimer(); resetTimer();
const treeDataProvider = new MyTreeDataProvider(); context.subscriptions.push(
vscode.commands.registerCommand("speed-code.submitSpeedRun", () => {
if (run) {
try {
const [isValid, res] = testCode(
fs.readFileSync(run.path).toString(),
[
{ input: ["racecar"], expected: true },
{ input: ["hello"], expected: false },
{ input: ["madam"], expected: true },
]
);
vscode.window.registerTreeDataProvider('myCustomTab', treeDataProvider); if (isValid) {
treeDataProvider.testResults = res;
treeDataProvider.testSummary = {
elapsed: run.elapsed,
valid: isValid,
};
resetTimer();
} else {
treeDataProvider.blunders.push([run.elapsed, res]);
}
context.subscriptions.push(vscode.commands.registerCommand('speed-code.submitSpeedRun', () => { treeDataProvider.refresh();
if (run) { } catch (e) {
try { treeDataProvider.blunders.push([run.elapsed, String(e)]);
const [isValid, res] = testCode(fs.readFileSync(run.path).toString(), [
{ input: ["racecar"], expected: true },
{ input: ["hello"], expected: false },
{ input: ["madam"], expected: true },
]);
treeDataProvider.testResults = res;
treeDataProvider.testSummary = { elapsed: run.elapsed, valid: isValid };
treeDataProvider.blunders = run.blunders;
treeDataProvider.refresh();
if (isValid) {
resetTimer();
} }
} catch {
// TODO: blunders
} }
} })
})); );
context.subscriptions.push(
vscode.commands.registerCommand("speed-code.startSpeedRun", () => {
treeDataProvider.testSummary = undefined;
treeDataProvider.testResults = [];
treeDataProvider.blunders = [];
const runFilePath = path.join(speedCodePath, "isPalindrome.js");
fs.writeFileSync(runFilePath, "");
openAndFocusFile(runFilePath);
context.subscriptions.push(vscode.commands.registerCommand('speed-code.startSpeedRun', () => { const startTime = Date.now();
const runFilePath = path.join(speedCodePath, 'isPalindrome.js'); run = {
fs.writeFileSync(runFilePath, ''); path: runFilePath,
openAndFocusFile(runFilePath); elapsed: 0,
timer: setInterval(() => {
if (!run) return;
const currentTime = Date.now();
run.elapsed = (currentTime - startTime) / 1000;
statusBarItem.text = utils.format(run.elapsed);
statusBarItem.color = "#fff";
}, 10),
};
const startTime = Date.now(); vscode.window.showInformationMessage("Speedrun Started!");
run = { }),
path: runFilePath, statusBarItem
elapsed: 0, );
blunders: [],
timer: setInterval(() => {
if (!run) return;
const currentTime = Date.now();
run.elapsed = (currentTime - startTime) / 1000;
statusBarItem.text = utils.format(run.elapsed);
statusBarItem.color = '#fff';
}, 10),
};
vscode.window.showInformationMessage('Speedrun Started!'); context.subscriptions.push(
}), statusBarItem); vscode.commands.registerCommand("speed-code.cancelSpeedRun", resetTimer),
statusBarItem
context.subscriptions.push(vscode.commands.registerCommand('speed-code.cancelSpeedRun', resetTimer), statusBarItem); );
} }
export function deactivate() { export function deactivate() {
+79 -21
View File
@@ -1,24 +1,41 @@
import * as vscode from 'vscode'; import * as vscode from "vscode";
import { TestResult } from './tester'; import { TestResult } from "./tester";
import { format } from './utils'; import { format } from "./utils";
class RunTreeItem extends vscode.TreeItem { class RunTreeItem extends vscode.TreeItem {
children?: RunTreeItem[]; children?: RunTreeItem[];
constructor(label: string | vscode.TreeItemLabel, description?: string, children?: RunTreeItem[], expanded?: boolean) { constructor(
super(label, children ? (expanded ? vscode.TreeItemCollapsibleState.Expanded : vscode.TreeItemCollapsibleState.Collapsed) : vscode.TreeItemCollapsibleState.None) label: string | vscode.TreeItemLabel,
description?: string,
children?: RunTreeItem[],
expanded: boolean = false
) {
super(
label,
children
? expanded
? vscode.TreeItemCollapsibleState.Expanded
: vscode.TreeItemCollapsibleState.Collapsed
: vscode.TreeItemCollapsibleState.None
);
this.children = children; this.children = children;
this.description = description; this.description = description;
} }
} }
export class MyTreeDataProvider implements vscode.TreeDataProvider<RunTreeItem> { export class MyTreeDataProvider
private _onDidChangeTreeData: vscode.EventEmitter<RunTreeItem | undefined | void> = new vscode.EventEmitter(); implements vscode.TreeDataProvider<RunTreeItem>
readonly onDidChangeTreeData: vscode.Event<RunTreeItem | undefined | void> = this._onDidChangeTreeData.event; {
private _onDidChangeTreeData: vscode.EventEmitter<
RunTreeItem | undefined | void
> = new vscode.EventEmitter();
readonly onDidChangeTreeData: vscode.Event<RunTreeItem | undefined | void> =
this._onDidChangeTreeData.event;
testResults: TestResult[] = []; testResults: TestResult[] = [];
testSummary: undefined | { elapsed: number, valid: boolean }; testSummary: undefined | { elapsed: number; valid: boolean };
blunders: [number, TestResult[]][] = []; blunders: [number, string | TestResult[]][] = [];
refresh(): void { refresh(): void {
this._onDidChangeTreeData.fire(); this._onDidChangeTreeData.fire();
@@ -33,20 +50,61 @@ export class MyTreeDataProvider implements vscode.TreeDataProvider<RunTreeItem>
const items: RunTreeItem[] = []; const items: RunTreeItem[] = [];
if (this.testSummary) { if (this.testSummary) {
items.push(new RunTreeItem(`Speedrun Summary`, undefined, [ items.push(
new RunTreeItem("Time Elapsed", format(this.testSummary.elapsed)), new RunTreeItem(
new RunTreeItem("Test Passed", this.testSummary.valid ? '✅ Passed' : '❌ Failed'), `Speedrun Summary`,
], true)); undefined,
[
new RunTreeItem("Time Elapsed", format(this.testSummary.elapsed)),
new RunTreeItem(
"Test Passed",
this.testSummary.valid ? "✅ Passed" : "❌ Failed"
),
new RunTreeItem("Blunders", `${this.blunders.length}`),
],
true
)
);
} }
// TODO: blunders if (this.blunders.length > 0) {
items.push(
new RunTreeItem(
`Blunders`,
undefined,
this.blunders.map((blunder, i) =>
typeof blunder[1] === "string"
? new RunTreeItem(blunder[1], format(blunder[0]))
: new RunTreeItem(
`Output Blunder ${i + 1}`,
format(blunder[0]),
blunder[1].map(
(t) =>
new RunTreeItem(
`(${JSON.stringify(t.input)})`,
undefined,
[
new RunTreeItem("Output", `${t.result}`),
new RunTreeItem("Expected Output", `${t.expected}`),
]
)
)
)
)
)
);
}
this.testResults.forEach(t => { this.testResults.forEach((t) => {
const item = new RunTreeItem(`(${JSON.stringify(t.input)})`, undefined, [ const item = new RunTreeItem(
new RunTreeItem('Output', `${t.result}`), `(${JSON.stringify(t.input)})`,
new RunTreeItem('Expected Output', `${t.expected}`), undefined,
]); [
item.description = t.valid ? '✅ Passed' : '❌ Failed'; new RunTreeItem("Output", `${t.result}`),
new RunTreeItem("Expected Output", `${t.expected}`),
]
);
item.description = t.valid ? "✅ Passed" : "❌ Failed";
items.push(item); items.push(item);
}); });