From 5d029967ed0a58356b15b786d8284d152bb20705 Mon Sep 17 00:00:00 2001 From: Theis Pieter Hollebeek Date: Fri, 6 Dec 2024 11:57:26 +0100 Subject: [PATCH] code cov/source code view --- web/public/.gitignore | 1 + web/public/deno.jsonc | 7 +- web/public/dist/bundle.js | 188 +++++---------------- web/public/dist/bytes.esm.js | 318 ----------------------------------- web/public/index.html | 4 +- web/public/src/index.ts | 196 +++++++++++---------- web/public/style.css | 44 ++--- 7 files changed, 171 insertions(+), 587 deletions(-) create mode 100644 web/public/.gitignore delete mode 100644 web/public/dist/bytes.esm.js diff --git a/web/public/.gitignore b/web/public/.gitignore new file mode 100644 index 0000000..849ddff --- /dev/null +++ b/web/public/.gitignore @@ -0,0 +1 @@ +dist/ diff --git a/web/public/deno.jsonc b/web/public/deno.jsonc index 801f699..97a7ef2 100644 --- a/web/public/deno.jsonc +++ b/web/public/deno.jsonc @@ -1,6 +1,11 @@ { + "tasks": { + "bundle": "deno run -A bundle.ts", + "dev": "deno run --watch -A bundle.ts" + }, "compilerOptions": { - "lib": ["dom", "dom.iterable", "dom.asynciterable"] + "checkJs": false, + "lib": ["dom", "dom.iterable", "dom.asynciterable", "deno.ns"] }, "fmt": { "indentWidth": 4 diff --git a/web/public/dist/bundle.js b/web/public/dist/bundle.js index e7c790d..dab7b11 100644 --- a/web/public/dist/bundle.js +++ b/web/public/dist/bundle.js @@ -1,27 +1,32 @@ // src/index.ts -var codeCoverageDiv = document.querySelector("#code-coverage"); -var flameGraphDiv = document.querySelector("#flame-graph"); -function drawText(text, codeCoverageData) { - const tooltip = document.getElementById("covers-tooltip"); +function loadCodeCoverage(text, codeCoverageData, codeCoverageDiv) { + const tooltip = document.createElement("span"); + tooltip.id = "covers-tooltip"; + codeCoverageDiv.append(tooltip); const entries = codeCoverageData.toSorted((a, b) => b.index - a.index); const charEntries = {}; const elements = []; let line = 1; let col = 1; for (let index = 0; index < text.length; ++index) { - if (text[index] == "\n") { + if (text[index] === "\n") { col = 1; line += 1; - elements.push("\n"); + const newlineSpan = document.createElement("span"); + newlineSpan.innerText = "\n"; + elements.push(newlineSpan); continue; } const entry = entries.find((entry2) => index >= entry2.index); + if (!entry) { + throw new Error("unreachable"); + } charEntries[`${line}-${col}`] = entry; const color = (ratio) => `rgba(${255 - 255 * ratio}, ${255 * ratio}, 125, 0.5)`; const span = document.createElement("span"); span.style.backgroundColor = color(Math.min(entry.covers / 25, 1)); span.innerText = text[index]; - span.dataset.covers = entry.covers; + span.dataset.covers = entry.covers.toString(); elements.push(span); col += 1; } @@ -30,16 +35,18 @@ function drawText(text, codeCoverageData) { const outside = x < boundingRect.left || x >= boundingRect.right || y < boundingRect.top || y >= boundingRect.bottom; return !outside; } - testytestytesty.append(...elements); + codeCoverageDiv.append(...elements); document.addEventListener("mousemove", (event) => { const [x, y] = [event.clientX, event.clientY]; - const outerBox = testytestytesty.getBoundingClientRect(); + const outerBox = codeCoverageDiv.getBoundingClientRect(); if (!positionInBox([x, y], outerBox)) { - console.log("+"); return; } const element = elements.find((element2) => { - if (!element2.dataset?.covers) { + if (typeof element2 === "string") { + return false; + } + if (!element2.dataset.covers) { return false; } const isIn = positionInBox([x, y], element2.getBoundingClientRect()); @@ -56,127 +63,8 @@ function drawText(text, codeCoverageData) { tooltip.innerText = `Ran ${covers} time${covers !== 1 ? "s" : ""}`; }); } -function loadCodeCoverage(text, codeCoverageData) { - codeCoverageDiv.innerHTML = ` - -
${text}
- - `; - const canvas = document.querySelector("#code-coverage-canvas"); - canvas.width = 1e3; - canvas.height = 500; - const ctx = canvas.getContext("2d"); - ctx.font = "20px monospace"; - const { width: chWidth } = ctx.measureText("-"); - const chHeight = 23; - const color = (ratio) => `rgba(${255 - 255 * ratio}, ${255 * ratio}, 125, 0.5)`; - const entries = codeCoverageData.toSorted((a, b) => b.index - a.index); - const charEntries = {}; - let line = 1; - let col = 1; - for (let index = 0; index < text.length; ++index) { - if (text[index] == "\n") { - col = 1; - line += 1; - continue; - } - const entry = entries.find((entry2) => index >= entry2.index); - charEntries[`${line}-${col}`] = entry; - ctx.fillStyle = color(Math.min(entry.covers / 25, 1)); - ctx.fillRect( - (col - 1) * chWidth, - (line - 1) * chHeight, - chWidth, - chHeight - ); - col += 1; - } - const tooltip = document.getElementById("covers-tooltip"); - canvas.addEventListener("mousemove", (e) => { - const col2 = Math.floor(e.offsetX / chWidth + 1); - const line2 = Math.floor(e.offsetY / chHeight + 1); - const key = `${line2}-${col2}`; - if (!(key in charEntries)) { - tooltip.hidden = true; - return; - } - const entry = charEntries[key]; - tooltip.innerText = `Ran ${entry.covers} time${entry.covers !== 1 ? "s" : ""}`; - tooltip.style.left = `${e.clientX + 20}px`; - tooltip.style.top = `${e.clientY + 20}px`; - tooltip.hidden = false; - }); - canvas.addEventListener("mouseleave", () => { - tooltip.hidden = true; - }); -} -function loadFlameGraph(flameGraphData, fnNames) { - flameGraphDiv.innerHTML = ` - - - `; - const canvas = document.querySelector("#flame-graph-canvas"); - canvas.width = 1e3; - canvas.height = 500; - const ctx = canvas.getContext("2d"); - ctx.font = "16px monospace"; - const nodes = []; - function calculateNodeRects(node, depth, totalAcc, offsetAcc) { - const x = offsetAcc / totalAcc * canvas.width; - const y = canvas.height - 30 * depth - 30; - const w = (node.acc + 1) / totalAcc * canvas.width; - const h = 30; - const title = fnNames[node.fn]; - const percent = `${(node.acc / totalAcc * 100).toFixed(1)}%`; - nodes.push({ x, y, w, h, title, percent }); - const totalChildrenAcc = node.children.reduce( - (acc, child) => acc + child.acc, - 0 - ); - let newOffsetAcc = offsetAcc + (node.acc - totalChildrenAcc) / 2; - for (const child of node.children) { - calculateNodeRects(child, depth + 1, totalAcc, newOffsetAcc); - newOffsetAcc += child.acc; - } - } - calculateNodeRects(flameGraphData, 0, flameGraphData.acc, 0); - for (const node of nodes) { - const { x, y, w, h, title } = node; - ctx.fillStyle = "rgb(255, 125, 0)"; - ctx.fillRect( - x + 1, - y + 1, - w - 2, - h - 2 - ); - ctx.fillStyle = "black"; - ctx.fillText( - title, - x + (w - 10) / 2 - ctx.measureText(title).width / 2 + 5, - y + 20 - ); - } - const tooltip = document.getElementById("flame-graph-tooltip"); - canvas.addEventListener("mousemove", (e) => { - const x = e.offsetX; - const y = e.offsetY; - const node = nodes.find( - (node2) => x >= node2.x && x < node2.x + node2.w && y >= node2.y && y < node2.y + node2.h - ); - if (!node) { - tooltip.hidden = true; - return; - } - tooltip.innerText = `${node.title} ${node.percent}`; - tooltip.style.left = `${e.clientX + 20}px`; - tooltip.style.top = `${e.clientY + 20}px`; - tooltip.hidden = false; - }); - canvas.addEventListener("mouseleave", () => { - tooltip.hidden = true; - }); -} -var codeData = `fn add(a, b) { +function main() { + const codeData = `fn add(a, b) { + a b } @@ -190,25 +78,41 @@ loop { i = + i 1; } `; -function main() { const codeCoverageData = JSON.parse( `[{"index":0,"line":1,"col":1,"covers":2},{"index":28,"line":5,"col":1,"covers":1},{"index":44,"line":6,"col":1,"covers":1},{"index":55,"line":7,"col":1,"covers":1},{"index":66,"line":8,"col":5,"covers":11},{"index":104,"line":11,"col":5,"covers":10},{"index":19,"line":2,"col":5,"covers":10},{"index":133,"line":12,"col":5,"covers":10},{"index":87,"line":9,"col":9,"covers":1}]` ); const flameGraphData = JSON.parse( `{"fn":0,"acc":257,"parent":0,"children":[{"fn":18,"acc":251,"parent":0,"children":[{"fn":12,"acc":30,"parent":1,"children":[]}]}]}` ); - const viewRadios = document.querySelectorAll('input[name="views"]'); + const view = document.querySelector("#view"); + const renderFunctions = { + "source-code": () => { + const code = document.createElement("pre"); + code.innerHTML = codeData; + view.replaceChildren(code); + }, + "code-coverage": () => { + const codeCoverageElement = document.createElement("pre"); + loadCodeCoverage(codeData, codeCoverageData, codeCoverageElement); + const view2 = document.querySelector("#view"); + view2.replaceChildren(codeCoverageElement); + }, + "flame-graph": () => { + } + }; + const viewRadios = document.querySelectorAll( + 'input[name="views"]' + ); for (const input of viewRadios) { input.addEventListener("input", (ev) => { - console.log(ev); + const target = ev.target; + const value = target.value; + renderFunctions[value](); }); + if (input.checked) { + const value = input.value; + renderFunctions[value](); + } } - loadCodeCoverage(codeData, codeCoverageData); - loadFlameGraph(flameGraphData, { - 0: "", - 12: "add", - 18: "main" - }); - drawText(codeData, codeCoverageData); } main(); diff --git a/web/public/dist/bytes.esm.js b/web/public/dist/bytes.esm.js deleted file mode 100644 index 893ea5f..0000000 --- a/web/public/dist/bytes.esm.js +++ /dev/null @@ -1,318 +0,0 @@ -// https://deno.land/std@0.185.0/bytes/bytes_list.ts -var BytesList = class { - #len = 0; - #chunks = []; - constructor() { - } - /** - * Total size of bytes - */ - size() { - return this.#len; - } - /** - * Push bytes with given offset infos - */ - add(value, start = 0, end = value.byteLength) { - if (value.byteLength === 0 || end - start === 0) { - return; - } - checkRange(start, end, value.byteLength); - this.#chunks.push({ - value, - end, - start, - offset: this.#len - }); - this.#len += end - start; - } - /** - * Drop head `n` bytes. - */ - shift(n) { - if (n === 0) { - return; - } - if (this.#len <= n) { - this.#chunks = []; - this.#len = 0; - return; - } - const idx = this.getChunkIndex(n); - this.#chunks.splice(0, idx); - const [chunk] = this.#chunks; - if (chunk) { - const diff = n - chunk.offset; - chunk.start += diff; - } - let offset = 0; - for (const chunk2 of this.#chunks) { - chunk2.offset = offset; - offset += chunk2.end - chunk2.start; - } - this.#len = offset; - } - /** - * Find chunk index in which `pos` locates by binary-search - * returns -1 if out of range - */ - getChunkIndex(pos) { - let max = this.#chunks.length; - let min = 0; - while (true) { - const i = min + Math.floor((max - min) / 2); - if (i < 0 || this.#chunks.length <= i) { - return -1; - } - const { offset, start, end } = this.#chunks[i]; - const len = end - start; - if (offset <= pos && pos < offset + len) { - return i; - } else if (offset + len <= pos) { - min = i + 1; - } else { - max = i - 1; - } - } - } - /** - * Get indexed byte from chunks - */ - get(i) { - if (i < 0 || this.#len <= i) { - throw new Error("out of range"); - } - const idx = this.getChunkIndex(i); - const { value, offset, start } = this.#chunks[idx]; - return value[start + i - offset]; - } - /** - * Iterator of bytes from given position - */ - *iterator(start = 0) { - const startIdx = this.getChunkIndex(start); - if (startIdx < 0) - return; - const first = this.#chunks[startIdx]; - let firstOffset = start - first.offset; - for (let i = startIdx; i < this.#chunks.length; i++) { - const chunk = this.#chunks[i]; - for (let j = chunk.start + firstOffset; j < chunk.end; j++) { - yield chunk.value[j]; - } - firstOffset = 0; - } - } - /** - * Returns subset of bytes copied - */ - slice(start, end = this.#len) { - if (end === start) { - return new Uint8Array(); - } - checkRange(start, end, this.#len); - const result = new Uint8Array(end - start); - const startIdx = this.getChunkIndex(start); - const endIdx = this.getChunkIndex(end - 1); - let written = 0; - for (let i = startIdx; i <= endIdx; i++) { - const { - value: chunkValue, - start: chunkStart, - end: chunkEnd, - offset: chunkOffset - } = this.#chunks[i]; - const readStart = chunkStart + (i === startIdx ? start - chunkOffset : 0); - const readEnd = i === endIdx ? end - chunkOffset + chunkStart : chunkEnd; - const len = readEnd - readStart; - result.set(chunkValue.subarray(readStart, readEnd), written); - written += len; - } - return result; - } - /** - * Concatenate chunks into single Uint8Array copied. - */ - concat() { - const result = new Uint8Array(this.#len); - let sum = 0; - for (const { value, start, end } of this.#chunks) { - result.set(value.subarray(start, end), sum); - sum += end - start; - } - return result; - } -}; -function checkRange(start, end, len) { - if (start < 0 || len < start || end < 0 || len < end || end < start) { - throw new Error("invalid range"); - } -} - -// https://deno.land/std@0.185.0/bytes/concat.ts -function concat(...buf) { - let length = 0; - for (const b of buf) { - length += b.length; - } - const output = new Uint8Array(length); - let index = 0; - for (const b of buf) { - output.set(b, index); - index += b.length; - } - return output; -} - -// https://deno.land/std@0.185.0/bytes/copy.ts -function copy(src, dst, off = 0) { - off = Math.max(0, Math.min(off, dst.byteLength)); - const dstBytesAvailable = dst.byteLength - off; - if (src.byteLength > dstBytesAvailable) { - src = src.subarray(0, dstBytesAvailable); - } - dst.set(src, off); - return src.byteLength; -} - -// https://deno.land/std@0.185.0/bytes/ends_with.ts -function endsWith(source, suffix) { - for (let srci = source.length - 1, sfxi = suffix.length - 1; sfxi >= 0; srci--, sfxi--) { - if (source[srci] !== suffix[sfxi]) - return false; - } - return true; -} - -// https://deno.land/std@0.185.0/bytes/equals.ts -function equalsNaive(a, b) { - for (let i = 0; i < b.length; i++) { - if (a[i] !== b[i]) - return false; - } - return true; -} -function equals32Bit(a, b) { - const len = a.length; - const compressable = Math.floor(len / 4); - const compressedA = new Uint32Array(a.buffer, 0, compressable); - const compressedB = new Uint32Array(b.buffer, 0, compressable); - for (let i = compressable * 4; i < len; i++) { - if (a[i] !== b[i]) - return false; - } - for (let i = 0; i < compressedA.length; i++) { - if (compressedA[i] !== compressedB[i]) - return false; - } - return true; -} -function equals(a, b) { - if (a.length !== b.length) { - return false; - } - return a.length < 1e3 ? equalsNaive(a, b) : equals32Bit(a, b); -} - -// https://deno.land/std@0.185.0/bytes/index_of_needle.ts -function indexOfNeedle(source, needle, start = 0) { - if (start >= source.length) { - return -1; - } - if (start < 0) { - start = Math.max(0, source.length + start); - } - const s = needle[0]; - for (let i = start; i < source.length; i++) { - if (source[i] !== s) - continue; - const pin = i; - let matched = 1; - let j = i; - while (matched < needle.length) { - j++; - if (source[j] !== needle[j - pin]) { - break; - } - matched++; - } - if (matched === needle.length) { - return pin; - } - } - return -1; -} - -// https://deno.land/std@0.185.0/bytes/includes_needle.ts -function includesNeedle(source, needle, start = 0) { - return indexOfNeedle(source, needle, start) !== -1; -} - -// https://deno.land/std@0.185.0/bytes/last_index_of_needle.ts -function lastIndexOfNeedle(source, needle, start = source.length - 1) { - if (start < 0) { - return -1; - } - if (start >= source.length) { - start = source.length - 1; - } - const e = needle[needle.length - 1]; - for (let i = start; i >= 0; i--) { - if (source[i] !== e) - continue; - const pin = i; - let matched = 1; - let j = i; - while (matched < needle.length) { - j--; - if (source[j] !== needle[needle.length - 1 - (pin - j)]) { - break; - } - matched++; - } - if (matched === needle.length) { - return pin - needle.length + 1; - } - } - return -1; -} - -// https://deno.land/std@0.185.0/bytes/repeat.ts -function repeat(source, count) { - if (count === 0) { - return new Uint8Array(); - } - if (count < 0) { - throw new RangeError("bytes: negative repeat count"); - } - if (!Number.isInteger(count)) { - throw new Error("bytes: repeat count must be an integer"); - } - const nb = new Uint8Array(source.length * count); - let bp = copy(source, nb); - for (; bp < nb.length; bp *= 2) { - copy(nb.slice(0, bp), nb, bp); - } - return nb; -} - -// https://deno.land/std@0.185.0/bytes/starts_with.ts -function startsWith(source, prefix) { - for (let i = 0, max = prefix.length; i < max; i++) { - if (source[i] !== prefix[i]) - return false; - } - return true; -} -export { - BytesList, - concat, - copy, - endsWith, - equals, - includesNeedle, - indexOfNeedle, - lastIndexOfNeedle, - repeat, - startsWith -}; diff --git a/web/public/index.html b/web/public/index.html index c0f4bce..f1a1026 100644 --- a/web/public/index.html +++ b/web/public/index.html @@ -26,11 +26,11 @@
-
-

+                    

                     
Process is currently running
+ diff --git a/web/public/src/index.ts b/web/public/src/index.ts index eb0fa47..342d206 100644 --- a/web/public/src/index.ts +++ b/web/public/src/index.ts @@ -1,59 +1,78 @@ -const codeCoverageDiv = document.querySelector("#code-coverage"); -const flameGraphDiv = document.querySelector("#flame-graph"); - type CodeCovEntry = { index: number; line: number; col: number; covers: number; }; -function drawText(text: string, codeCoverageData: CodeCovEntry[]) { - const tooltip = document.getElementById("covers-tooltip"); + +function loadCodeCoverage( + text: string, + codeCoverageData: CodeCovEntry[], + codeCoverageDiv: HTMLPreElement, +) { + const tooltip = document.createElement("span"); + tooltip.id = "covers-tooltip"; + codeCoverageDiv.append(tooltip); const entries = codeCoverageData.toSorted(( a: CodeCovEntry, b: CodeCovEntry, ) => b.index - a.index); - const charEntries = {}; - const elements = []; + const charEntries: { [key: string]: CodeCovEntry } = {}; + const elements: HTMLElement[] = []; let line = 1; let col = 1; for (let index = 0; index < text.length; ++index) { - if (text[index] == "\n") { + if (text[index] === "\n") { col = 1; line += 1; - elements.push("\n"); + const newlineSpan = document.createElement("span"); + newlineSpan.innerText = "\n"; + elements.push(newlineSpan); continue; } const entry = entries.find((entry) => index >= entry.index); + if (!entry) { + throw new Error("unreachable"); + } charEntries[`${line}-${col}`] = entry; - const color = (ratio) => + const color = (ratio: number) => `rgba(${255 - 255 * ratio}, ${255 * ratio}, 125, 0.5)`; const span = document.createElement("span"); span.style.backgroundColor = color(Math.min(entry.covers / 25, 1)); span.innerText = text[index]; - span.dataset.covers = entry.covers; + span.dataset.covers = entry.covers.toString(); elements.push(span); col += 1; } - function positionInBox(position, boundingRect) { + function positionInBox( + position: [number, number], + boundingRect: { + left: number; + top: number; + right: number; + bottom: number; + }, + ) { const [x, y] = position; const outside = x < boundingRect.left || x >= boundingRect.right || y < boundingRect.top || y >= boundingRect.bottom; return !outside; } - testytestytesty.append(...elements); + codeCoverageDiv.append(...elements); document.addEventListener("mousemove", (event) => { const [x, y] = [event.clientX, event.clientY]; - const outerBox = testytestytesty.getBoundingClientRect(); + const outerBox = codeCoverageDiv.getBoundingClientRect(); if (!positionInBox([x, y], outerBox)) { - console.log("+"); return; } const element = elements.find((element) => { - if (!element.dataset?.covers) { + if (typeof element === "string") { + return false; + } + if (!element.dataset.covers) { return false; } const isIn = positionInBox([x, y], element.getBoundingClientRect()); @@ -71,91 +90,34 @@ function drawText(text: string, codeCoverageData: CodeCovEntry[]) { }); } -function loadCodeCoverage(text, codeCoverageData) { - codeCoverageDiv.innerHTML = ` - -
${text}
- - `; - - /** @type { HTMLCanvasElement } */ - const canvas = document.querySelector("#code-coverage-canvas"); - canvas.width = 1000; - canvas.height = 500; - - const ctx = canvas.getContext("2d"); - - ctx.font = "20px monospace"; - - const { width: chWidth } = ctx.measureText("-"); - const chHeight = 23; - - const color = (ratio) => - `rgba(${255 - 255 * ratio}, ${255 * ratio}, 125, 0.5)`; - - const entries = codeCoverageData.toSorted((a, b) => b.index - a.index); - - const charEntries = {}; - - let line = 1; - let col = 1; - for (let index = 0; index < text.length; ++index) { - if (text[index] == "\n") { - col = 1; - line += 1; - continue; - } - const entry = entries.find((entry) => index >= entry.index); - charEntries[`${line}-${col}`] = entry; - ctx.fillStyle = color(Math.min(entry.covers / 25, 1)); - ctx.fillRect( - (col - 1) * chWidth, - (line - 1) * chHeight, - chWidth, - chHeight, - ); - col += 1; - } - - const tooltip = document.getElementById("covers-tooltip"); - - canvas.addEventListener("mousemove", (e) => { - const col = Math.floor(e.offsetX / chWidth + 1); - const line = Math.floor(e.offsetY / chHeight + 1); - const key = `${line}-${col}`; - if (!(key in charEntries)) { - tooltip.hidden = true; - return; - } - const entry = charEntries[key]; - tooltip.innerText = `Ran ${entry.covers} time${ - entry.covers !== 1 ? "s" : "" - }`; - tooltip.style.left = `${e.clientX + 20}px`; - tooltip.style.top = `${e.clientY + 20}px`; - tooltip.hidden = false; - }); - canvas.addEventListener("mouseleave", () => { - tooltip.hidden = true; - }); -} - -function loadFlameGraph(flameGraphData, fnNames) { +// @ts-ignore: unsure of relevant types +function loadFlameGraph(flameGraphData, fnNames, flameGraphDiv) { flameGraphDiv.innerHTML = ` `; - /** @type { HTMLCanvasElement } */ - const canvas = document.querySelector("#flame-graph-canvas"); + const canvas = document.querySelector( + "#flame-graph-canvas", + )!; canvas.width = 1000; canvas.height = 500; - const ctx = canvas.getContext("2d"); + const ctx = canvas.getContext("2d")!; ctx.font = "16px monospace"; - const nodes = []; + type Node = { + x: number; + y: number; + w: number; + h: number; + title: string; + percent: string; + }; + const nodes: Node[] = []; + + // @ts-ignore: unsure of relevant types function calculateNodeRects(node, depth, totalAcc, offsetAcc) { const x = (offsetAcc / totalAcc) * canvas.width; const y = canvas.height - 30 * depth - 30; @@ -167,6 +129,7 @@ function loadFlameGraph(flameGraphData, fnNames) { nodes.push({ x, y, w, h, title, percent }); const totalChildrenAcc = node.children.reduce( + // @ts-ignore: unsure of relevant types (acc, child) => acc + child.acc, 0, ); @@ -195,7 +158,7 @@ function loadFlameGraph(flameGraphData, fnNames) { ); } - const tooltip = document.getElementById("flame-graph-tooltip"); + const tooltip = document.getElementById("flame-graph-tooltip")!; canvas.addEventListener("mousemove", (e) => { const x = e.offsetX; @@ -219,7 +182,8 @@ function loadFlameGraph(flameGraphData, fnNames) { }); } -const codeData = `\ +function main() { + const codeData = `\ fn add(a, b) { + a b } @@ -235,7 +199,6 @@ loop { } `; -function main() { const codeCoverageData = JSON.parse( `[{"index":0,"line":1,"col":1,"covers":2},{"index":28,"line":5,"col":1,"covers":1},{"index":44,"line":6,"col":1,"covers":1},{"index":55,"line":7,"col":1,"covers":1},{"index":66,"line":8,"col":5,"covers":11},{"index":104,"line":11,"col":5,"covers":10},{"index":19,"line":2,"col":5,"covers":10},{"index":133,"line":12,"col":5,"covers":10},{"index":87,"line":9,"col":9,"covers":1}]`, ); @@ -244,20 +207,49 @@ function main() { `{"fn":0,"acc":257,"parent":0,"children":[{"fn":18,"acc":251,"parent":0,"children":[{"fn":12,"acc":30,"parent":1,"children":[]}]}]}`, ); - const viewRadios = document.querySelectorAll('input[name="views"]'); + type RenderFns = { + "source-code": () => void; + "code-coverage": () => void; + "flame-graph": () => void; + }; + + const view = document.querySelector("#view")!; + const renderFunctions: RenderFns = { + "source-code": () => { + const code = document.createElement("pre"); + code.innerHTML = codeData; + view.replaceChildren(code); + }, + "code-coverage": () => { + const codeCoverageElement = document.createElement("pre"); + loadCodeCoverage(codeData, codeCoverageData, codeCoverageElement); + const view = document.querySelector("#view")!; + view.replaceChildren(codeCoverageElement); + }, + "flame-graph": () => {}, + }; + + const viewRadios: NodeListOf = document.querySelectorAll( + 'input[name="views"]', + ); for (const input of viewRadios) { input.addEventListener("input", (ev) => { - console.log(ev); + const target = ev.target as HTMLInputElement; + const value = target.value as keyof RenderFns; + renderFunctions[value](); }); + if (input.checked) { + const value = input.value as keyof RenderFns; + renderFunctions[value](); + } } - loadCodeCoverage(codeData, codeCoverageData); - loadFlameGraph(flameGraphData, { - 0: "", - 12: "add", - 18: "main", - }); - drawText(codeData, codeCoverageData); + // loadCodeCoverage(codeData, codeCoverageData); + // loadFlameGraph(flameGraphData, { + // 0: "", + // 12: "add", + // 18: "main", + // }); } main(); diff --git a/web/public/style.css b/web/public/style.css index 8f726f0..b47a0f7 100644 --- a/web/public/style.css +++ b/web/public/style.css @@ -77,6 +77,27 @@ main #cover { gap: 0.5rem; } +#views-nav input { + display: none; +} + +#views-nav label { + display: inline-block; + padding: 0.4em; + padding-bottom: 0.2em; + cursor: pointer; +} + +#view pre { + font-size: 1rem; +} + +#views-nav input:checked + label { + background-color: var(--code-status); + color: var(--black); +} + + #views-layout { display: flex; margin: 0 auto; @@ -85,28 +106,7 @@ main #cover { max-width: 1500px; } -#code-coverage { - width: 1000px; - height: 500px; - background-color: rgb(240, 220, 200); -} -#code-coverage pre { - background-color: none; -} -#code-coverage code { - font-family: monospace; - color: black; - font-weight: 600; - font-size: 20px; -} -#code-coverage canvas { - z-index: 1; - width: 1000px; - height: 500px; - position: absolute; - image-rendering: pixelated; -} -#code-coverage #covers-tooltip { +#covers-tooltip { z-index: 2; position: fixed; top: 0;