From fd01ccf8af86c61923f9be18692d3c8a3d9149f3 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Wed, 6 Aug 2025 15:08:57 -0500 Subject: [PATCH] Working analytics --- package.json | 21 +++++++++++++++- src/extension.ts | 33 ++++++++++++++++++-------- src/resultViewer.ts | 58 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 11 deletions(-) create mode 100644 src/resultViewer.ts diff --git a/package.json b/package.json index 615e289..df93d80 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,26 @@ "key": "shift+enter", "when": "editorTextFocus" } - ] + ], + "viewsContainers": { + "panel": [ + { + "id": "myCustomPanel", + "title": "My Custom Panel", + "icon": "$(file-code)" + } + ] + }, + "views": { + "myCustomPanel": [ + { + "type": "tree", + "id": "myCustomTab", + "name": "My Tab", + "icon": "$(file-code)" + } + ] + } }, "scripts": { "vscode:prepublish": "tsc -p ./", diff --git a/src/extension.ts b/src/extension.ts index 5e11881..26dbf63 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -2,8 +2,9 @@ import * as vscode from 'vscode'; import os from 'os'; import path from 'path'; import fs from 'fs'; -import testCode from './tester'; +import testCode, { TestResult } from './tester'; import * as utils from './utils'; +import { MyTreeDataProvider } from './resultViewer'; let resetTimer = () => {} @@ -30,7 +31,7 @@ export function activate(context: vscode.ExtensionContext) { statusBarItem.text = "No active Speedrun"; statusBarItem.show(); - let run: undefined | { path: string, elapsed: number, timer: NodeJS.Timeout }; + let run: undefined | { path: string, elapsed: number, timer: NodeJS.Timeout, blunders: [number, TestResult[]][] }; resetTimer = () => { if (run) { @@ -42,18 +43,29 @@ export function activate(context: vscode.ExtensionContext) { resetTimer(); + const treeDataProvider = new MyTreeDataProvider(); + + vscode.window.registerTreeDataProvider('myCustomTab', treeDataProvider); + context.subscriptions.push(vscode.commands.registerCommand('speed-code.submitSpeedRun', () => { if (run) { - const [isValid, res] = testCode(fs.readFileSync(run.path).toString(), [ - { input: ["racecar"], expected: true }, - { input: ["hello"], expected: true }, - { input: ["madam"], expected: true }, - ]); + try { + const [isValid, res] = testCode(fs.readFileSync(run.path).toString(), [ + { input: ["racecar"], expected: true }, + { input: ["hello"], expected: false }, + { input: ["madam"], expected: true }, + ]); - vscode.window.showInformationMessage('Time: ' + run.elapsed); + treeDataProvider.testResults = res; + treeDataProvider.testSummary = { elapsed: run.elapsed, valid: isValid }; + treeDataProvider.blunders = run.blunders; + treeDataProvider.refresh(); - if (isValid) { - resetTimer(); + if (isValid) { + resetTimer(); + } + } catch { + // TODO: blunders } } })); @@ -68,6 +80,7 @@ export function activate(context: vscode.ExtensionContext) { run = { path: runFilePath, elapsed: 0, + blunders: [], timer: setInterval(() => { if (!run) return; const currentTime = Date.now(); diff --git a/src/resultViewer.ts b/src/resultViewer.ts new file mode 100644 index 0000000..f68cbb2 --- /dev/null +++ b/src/resultViewer.ts @@ -0,0 +1,58 @@ +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) + 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; + + testResults: TestResult[] = []; + testSummary: undefined | { elapsed: number, valid: boolean }; + blunders: [number, TestResult[]][] = []; + + refresh(): void { + this._onDidChangeTreeData.fire(); + } + + getTreeItem(element: RunTreeItem): RunTreeItem { + return element; + } + + getChildren(element?: RunTreeItem): Thenable { + if (!element) { + 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)); + } + + // TODO: blunders + + 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); + }); + + return Promise.resolve(items); + } else { + return Promise.resolve(element.children || []); + } + } +}