From 272812a815026a22a031cbf26c325adb35e4eb44 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Wed, 6 Aug 2025 22:52:32 +0200 Subject: [PATCH] blunders --- src/extension.ts | 138 ++++++++++++++++++++++++++------------------ src/resultViewer.ts | 100 +++++++++++++++++++++++++------- 2 files changed, 162 insertions(+), 76 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 26dbf63..58583c4 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,12 +1,12 @@ -import * as vscode from 'vscode'; -import os from 'os'; -import path from 'path'; -import fs from 'fs'; -import testCode, { TestResult } from './tester'; -import * as utils from './utils'; -import { MyTreeDataProvider } from './resultViewer'; +import * as vscode from "vscode"; +import os from "os"; +import path from "path"; +import fs from "fs"; +import testCode, { TestResult } from "./tester"; +import * as utils from "./utils"; +import { MyTreeDataProvider } from "./resultViewer"; -let resetTimer = () => {} +let resetTimer = () => {}; const speedCodePath = path.join(os.homedir(), ".speed-code/"); @@ -16,7 +16,10 @@ if (!fs.existsSync(speedCodePath)) { export async function openAndFocusFile(filePath: string) { 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 = ` @@ -26,12 +29,25 @@ function isPalindrome(s) { `; export function activate(context: vscode.ExtensionContext) { - const statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 100); - statusBarItem.tooltip = 'Speedrun timer'; + const statusBarItem = vscode.window.createStatusBarItem( + vscode.StatusBarAlignment.Left, + 100 + ); + statusBarItem.tooltip = "Speedrun timer"; statusBarItem.text = "No active Speedrun"; 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 = () => { if (run) { @@ -43,57 +59,69 @@ export function activate(context: vscode.ExtensionContext) { 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', () => { - if (run) { - try { - 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(); + treeDataProvider.refresh(); + } catch (e) { + treeDataProvider.blunders.push([run.elapsed, String(e)]); } - } 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 runFilePath = path.join(speedCodePath, 'isPalindrome.js'); - fs.writeFileSync(runFilePath, ''); - openAndFocusFile(runFilePath); + const startTime = Date.now(); + run = { + path: 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(); - run = { - path: runFilePath, - 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!"); + }), + statusBarItem + ); - vscode.window.showInformationMessage('Speedrun Started!'); - }), statusBarItem); - - context.subscriptions.push(vscode.commands.registerCommand('speed-code.cancelSpeedRun', resetTimer), statusBarItem); + context.subscriptions.push( + vscode.commands.registerCommand("speed-code.cancelSpeedRun", resetTimer), + statusBarItem + ); } export function deactivate() { diff --git a/src/resultViewer.ts b/src/resultViewer.ts index f68cbb2..9ff180b 100644 --- a/src/resultViewer.ts +++ b/src/resultViewer.ts @@ -1,24 +1,41 @@ -import * as vscode from 'vscode'; -import { TestResult } from './tester'; -import { format } from './utils'; +import * as vscode from "vscode"; +import { TestResult } from "./tester"; +import { format } from "./utils"; class RunTreeItem extends vscode.TreeItem { children?: RunTreeItem[]; - constructor(label: string | vscode.TreeItemLabel, description?: string, children?: RunTreeItem[], expanded?: boolean) { - super(label, children ? (expanded ? vscode.TreeItemCollapsibleState.Expanded : vscode.TreeItemCollapsibleState.Collapsed) : vscode.TreeItemCollapsibleState.None) + constructor( + 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.description = description; } } -export class MyTreeDataProvider implements vscode.TreeDataProvider { - private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); - readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; +export class MyTreeDataProvider + implements vscode.TreeDataProvider +{ + private _onDidChangeTreeData: vscode.EventEmitter< + RunTreeItem | undefined | void + > = new vscode.EventEmitter(); + readonly onDidChangeTreeData: vscode.Event = + this._onDidChangeTreeData.event; testResults: TestResult[] = []; - testSummary: undefined | { elapsed: number, valid: boolean }; - blunders: [number, TestResult[]][] = []; + testSummary: undefined | { elapsed: number; valid: boolean }; + blunders: [number, string | TestResult[]][] = []; refresh(): void { this._onDidChangeTreeData.fire(); @@ -33,20 +50,61 @@ export class MyTreeDataProvider implements vscode.TreeDataProvider const items: RunTreeItem[] = []; if (this.testSummary) { - items.push(new RunTreeItem(`Speedrun Summary`, undefined, [ - new RunTreeItem("Time Elapsed", format(this.testSummary.elapsed)), - new RunTreeItem("Test Passed", this.testSummary.valid ? '✅ Passed' : '❌ Failed'), - ], true)); + items.push( + new RunTreeItem( + `Speedrun Summary`, + 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 => { - const item = new RunTreeItem(`(${JSON.stringify(t.input)})`, undefined, [ - new RunTreeItem('Output', `${t.result}`), - new RunTreeItem('Expected Output', `${t.expected}`), - ]); - item.description = t.valid ? '✅ Passed' : '❌ Failed'; + this.testResults.forEach((t) => { + const item = new RunTreeItem( + `(${JSON.stringify(t.input)})`, + undefined, + [ + new RunTreeItem("Output", `${t.result}`), + new RunTreeItem("Expected Output", `${t.expected}`), + ] + ); + item.description = t.valid ? "✅ Passed" : "❌ Failed"; items.push(item); });