From 7cae5c67215b292f8b9ce50bb89b51b63d6bb1a4 Mon Sep 17 00:00:00 2001 From: Justin Bossis Date: Sun, 1 Sep 2024 15:38:40 +0200 Subject: [PATCH] Add coverage summary view --- package.json | 29 ++++++++++++++--- resources/icon.svg | 7 +++++ src/coverageitem.ts | 19 ++++++++++++ src/extension.ts | 76 ++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 125 insertions(+), 6 deletions(-) create mode 100644 resources/icon.svg create mode 100644 src/coverageitem.ts diff --git a/package.json b/package.json index c874521..fa3b0d3 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "name": "coveragetool", "displayName": "Coverage Tool", - "publisher": "justinbossis", - "description": "Highlights coverage.py lines in VSCode", - "version": "0.1.0", + "publisher": "justinbossis", + "description": "Highlights coverage.py lines in VSCode", + "version": "0.2.0", "engines": { "vscode": "^1.92.0" }, @@ -68,10 +68,29 @@ } } }, + "viewsContainers": { + "activitybar": [ + { + "id": "coveragetool", + "title": "Coverage Tool", + "icon": "resources/icon.svg" + } + ] + }, + "views": { + "coveragetool": [ + { + "id": "coveragetoolView", + "name": "Coverage Tool", + "order": 10, + "icon": "resources/icon.svg" + } + ] + }, "commands": [ { - "command": "coverage-tool.helloWorld", - "title": "Hello World" + "command": "coveragetool.refreshEntry", + "title": "Refresh Coverage" } ] }, diff --git a/resources/icon.svg b/resources/icon.svg new file mode 100644 index 0000000..aee1666 --- /dev/null +++ b/resources/icon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/coverageitem.ts b/src/coverageitem.ts new file mode 100644 index 0000000..2968026 --- /dev/null +++ b/src/coverageitem.ts @@ -0,0 +1,19 @@ +import { TreeItem, TreeItemCollapsibleState, Uri } from 'vscode'; + +export default class CoverageFile extends TreeItem { + constructor( + public readonly label: string, + public readonly relativePath: string, + public readonly filePath: string, + public readonly numberOfLines: number, + public readonly collapsibleState: TreeItemCollapsibleState, + ) { + super(label, collapsibleState); + this.description = `${numberOfLines} missing lines | ${relativePath}`; + this.command = { + command: 'vscode.open', + title: '', + arguments: [Uri.file(this.filePath)], + }; + } +} diff --git a/src/extension.ts b/src/extension.ts index 0447e1d..cf37972 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -9,6 +9,7 @@ import CoverageStatusBarItem from './statusBar/CoverageStatusBarItem'; import { CoverageStats, ICoverageCache, ICoverageStatsJson, IFileDecorationCache, IFileDecorationRange } from './models'; import Logger from './util/Logger'; import Configs from './config/Configs'; +import CoverageFile from './coverageitem'; // Promisified Functions const readFileAsync = promisify(readFile); @@ -21,6 +22,7 @@ const isWindows = platform() === 'win32'; let fileWatchers: vscode.FileSystemWatcher[] = []; let COV_CACHE: ICoverageCache = {}; let FILE_CACHE: IFileDecorationCache = {}; +let GLOBAL_COVERAGE: number = 0; // Functions @@ -62,6 +64,7 @@ function processJsonCoverage(json: any) { const covData: ICoverageCache = {}; if (json && json.files) { + GLOBAL_COVERAGE = parseInt(json.totals.percent_covered_display); // Look for the 'files' key in coverage JSON, and iterate through each file Object.keys(json.files).forEach((file: string) => { // Create CoverageStats for each file and assign to covData @@ -94,9 +97,9 @@ async function updateCache(files: vscode.Uri) { if (data && Object.keys(data).length > 0) { FILE_CACHE = {}; COV_CACHE = processJsonCoverage(data); + vscode.commands.executeCommand('coveragetool.refreshEntry'); return; } - Logger.log('No data found, could not update cache'); } } @@ -204,6 +207,63 @@ async function setupCacheAndWatchers() { setupFileWatchers(files); } +export class CoverageTreeViewProvider implements vscode.TreeDataProvider { + constructor(private workspaceRoot: string | undefined) {} + + private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); + readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; + + refresh(treeView: vscode.TreeView): void { + this._onDidChangeTreeData.fire(); + this.updateBadge(treeView); + } + + + getTreeItem(element: CoverageFile): vscode.TreeItem { + return element; + } + + getChildren(element?: CoverageFile): Thenable { + if (!this.workspaceRoot) { + vscode.window.showInformationMessage('No missing coverage found'); + return Promise.resolve([]); + } + if (element) { + return Promise.resolve([]); + } + else{ + return Promise.resolve(this.getFilesInCoverageJson()); + } + } + + private getFilesInCoverageJson(): CoverageFile[] { + if (COV_CACHE) { + const toFile = (fileName: string, relativePath: string, filePath: string, numberOfLines: number): CoverageFile => { + return new CoverageFile(fileName, relativePath, filePath, numberOfLines, vscode.TreeItemCollapsibleState.None); + }; + + const files = Object.keys(COV_CACHE).map((file) => { + const stats = COV_CACHE[file]; + return toFile(stats.path.split("/").pop() || "", stats.path, `${this.workspaceRoot}/${stats.path}`, stats.summary.missingLines); + }); + return files.filter((file) => file.numberOfLines !== 0); + } else { + return []; + } + } + private updateBadge(treeView: vscode.TreeView): void { + if (GLOBAL_COVERAGE !== 100){ + treeView.badge = { + value: GLOBAL_COVERAGE, + tooltip: 'Coverage percentage' + }; + }else{ + treeView.badge = undefined; + } + } + +} + // This method is called when your extension is activated // Your extension is activated the very first time the command is executed export async function activate(context: vscode.ExtensionContext) { @@ -223,6 +283,20 @@ export async function activate(context: vscode.ExtensionContext) { } }); + const rootPath = vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0 + ? vscode.workspace.workspaceFolders[0].uri.fsPath + : undefined; + const coverageTreeViewProvider = new CoverageTreeViewProvider(rootPath); + // vscode.window.registerTreeDataProvider('coveragetoolView', coverageTreeViewProvider); + const treeView = vscode.window.createTreeView('coveragetoolView', { + treeDataProvider: coverageTreeViewProvider + }); + + context.subscriptions.push(treeView); + vscode.commands.registerCommand('coveragetool.refreshEntry', () => + coverageTreeViewProvider.refresh(treeView) + ); + } // This method is called when your extension is deactivated