import * as data from "./data.ts"; type Color = { r: number; g: number; b: number }; function lerp2(ratio: number, start: number, end: number) { return (1 - ratio) * start + ratio * end; } function lerp3(ratio: number, start: number, middle: number, end: number) { return (1 - ratio) * lerp2(ratio, start, middle) + ratio * lerp2(ratio, middle, end); } function colorLerp( ratio: number, start: Color, middle: Color, end: Color, ): Color { return { r: lerp3(ratio, start.r, middle.r, end.r), g: lerp3(ratio, start.g, middle.g, end.g), b: lerp3(ratio, start.b, middle.b, end.b), }; } export function loadCodeCoverage( text: string, input: data.CodeCovEntry[], container: HTMLPreElement, tooltip: HTMLElement, ) { if (input.length === 0) { return; } const entries = input.toSorted(( a: data.CodeCovEntry, b: data.CodeCovEntry, ) => b.index - a.index); const charEntries: { [key: string]: data.CodeCovEntry } = {}; const elements: HTMLElement[] = []; let line = 1; let col = 1; const maxCovers = entries.map((v) => v.covers).reduce((acc, v) => acc > Math.log10(v) ? acc : Math.log10(v) ); for (let index = 0; index < text.length; ++index) { if (text[index] === "\n") { col = 1; line += 1; 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 backgroundColor = (ratio: number) => { const clr = colorLerp(ratio, { r: 42, g: 121, b: 82 }, { r: 247, g: 203, b: 21, }, { r: 167, g: 29, b: 49, }); return `rgb(${clr.r}, ${clr.g}, ${clr.b})`; }; const span = document.createElement("span"); span.style.backgroundColor = backgroundColor( Math.log10(entry.covers) / maxCovers, ); span.innerText = text[index]; span.dataset.covers = entry.covers.toString(); elements.push(span); col += 1; } 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; } container.append(...elements); document.addEventListener("mousemove", (event) => { const [x, y] = [event.clientX, event.clientY]; const outerBox = container.getBoundingClientRect(); if (!positionInBox([x, y], outerBox)) { tooltip.hidden = true; return; } const element = elements.find((element) => { if (typeof element === "string") { return false; } if (!element.dataset.covers) { return false; } const isIn = positionInBox([x, y], element.getBoundingClientRect()); return isIn; }); if (!element) { tooltip.hidden = true; return; } const maybeCovers = element.dataset.covers; if (!maybeCovers) { throw new Error("unreachable"); } const covers = parseInt(maybeCovers); tooltip.hidden = false; tooltip.style.left = `${event.clientX + 20}px`; tooltip.style.top = `${event.clientY + 20}px`; tooltip.innerText = `Ran ${covers} time${covers !== 1 ? "s" : ""}`; }); }