From 5591b628dbc7d38069e92ebd442ec9d984e83d06 Mon Sep 17 00:00:00 2001 From: sfja Date: Sun, 15 Dec 2024 00:07:36 +0100 Subject: [PATCH] fix flame graph --- compiler/assembler.ts | 27 +++--- compiler/ast.ts | 7 -- compiler/checker.ts | 6 ++ compiler/compiler.ts | 55 ++++++++++++ compiler/lib.ts | 6 ++ compiler/lowerer.ts | 20 +++-- compiler/main.ts | 49 +--------- compiler/mod.ts | 7 +- compiler/temputils.ts | 31 ------- slige-build-run-all.sh | 20 +++++ web/deno.lock | 3 +- web/main.ts | 25 +++--- web/public/src/data.ts | 8 ++ web/public/src/index.ts | 195 +++++++++++++++++++++++++++------------- web/public/style.css | 80 +++++++++++++---- web/runtime.ts | 42 --------- 16 files changed, 341 insertions(+), 240 deletions(-) create mode 100644 compiler/compiler.ts create mode 100644 compiler/lib.ts delete mode 100644 compiler/temputils.ts create mode 100755 slige-build-run-all.sh diff --git a/compiler/assembler.ts b/compiler/assembler.ts index 75024e8..a5a3d88 100644 --- a/compiler/assembler.ts +++ b/compiler/assembler.ts @@ -8,6 +8,9 @@ export type Label = { label: string }; export type Lit = number | string | boolean | Label; +export type Locs = { [key: string]: number }; +export type Refs = { [key: number]: string }; + export class Assembler { private lines: Line[] = []; private addedLabels: string[] = []; @@ -42,12 +45,12 @@ export class Assembler { this.addedLabels.push(label); } - public assemble(): number[] { + public assemble(): { program: number[]; locs: Locs } { console.log("Assembling..."); let ip = 0; - const output: number[] = []; - const locs: { [key: string]: number } = {}; - const refs: { [key: number]: string } = {}; + const program: number[] = []; + const locs: Locs = {}; + const refs: Refs = {}; let selectedLabel = ""; for (const line of this.lines) { @@ -62,20 +65,20 @@ export class Assembler { } for (const lit of line.ins as Lit[]) { if (typeof lit === "number") { - output.push(lit); + program.push(lit); ip += 1; } else if (typeof lit === "boolean") { - output.push(lit ? 1 : 0); + program.push(lit ? 1 : 0); ip += 1; } else if (typeof lit === "string") { - output.push(lit.length); + program.push(lit.length); ip += 1; for (let i = 0; i < lit.length; ++i) { - output.push(lit.charCodeAt(i)); + program.push(lit.charCodeAt(i)); ip += 1; } } else { - output.push(0); + program.push(0); refs[ip] = lit.label.startsWith(".") ? `${selectedLabel}${lit.label}` : refs[ip] = lit.label; @@ -83,7 +86,7 @@ export class Assembler { } } } - for (let i = 0; i < output.length; ++i) { + for (let i = 0; i < program.length; ++i) { if (!(i in refs)) { continue; } @@ -93,9 +96,9 @@ export class Assembler { ); continue; } - output[i] = locs[refs[i]]; + program[i] = locs[refs[i]]; } - return output; + return { program, locs }; } public printProgram() { diff --git a/compiler/ast.ts b/compiler/ast.ts index f4a5863..2fdc401 100644 --- a/compiler/ast.ts +++ b/compiler/ast.ts @@ -1,13 +1,6 @@ import { Pos } from "./token.ts"; import { VType } from "./vtype.ts"; -export type Ast = File[]; - -export type File = { - path: string; - stmts: Stmt[]; -}; - export type Stmt = { kind: StmtKind; pos: Pos; diff --git a/compiler/checker.ts b/compiler/checker.ts index a8b7451..884fca0 100644 --- a/compiler/checker.ts +++ b/compiler/checker.ts @@ -287,6 +287,12 @@ export class Checker { return this.checkIfExpr(expr); case "loop": return this.checkLoopExpr(expr); + case "while": + case "for_in": + case "for": + throw new Error( + "invalid ast: special loops should be desugered", + ); case "block": return this.checkBlockExpr(expr); } diff --git a/compiler/compiler.ts b/compiler/compiler.ts new file mode 100644 index 0000000..ec8666e --- /dev/null +++ b/compiler/compiler.ts @@ -0,0 +1,55 @@ +import { AstCreator } from "./ast.ts"; +import { Checker } from "./checker.ts"; +import { CompoundAssignDesugarer } from "./desugar/compound_assign.ts"; +import { SpecialLoopDesugarer } from "./desugar/special_loop.ts"; +import { Reporter } from "./info.ts"; +import { Lexer } from "./lexer.ts"; +import { FnNamesMap, Lowerer } from "./lowerer.ts"; +import { Parser } from "./parser.ts"; +import { Resolver } from "./resolver.ts"; + +export type CompiledFile = { + filepath: string; + program: number[]; +}; + +export type CompileResult = { + program: number[]; + fnNames: FnNamesMap; +}; + +export class Compiler { + private astCreator = new AstCreator(); + private reporter = new Reporter(); + + public constructor(private startFilePath: string) {} + + public async compile(): Promise { + const text = await Deno.readTextFile(this.startFilePath); + + const lexer = new Lexer(text, this.reporter); + + const parser = new Parser(lexer, this.astCreator, this.reporter); + const ast = parser.parse(); + + new SpecialLoopDesugarer(this.astCreator).desugar(ast); + + new Resolver(this.reporter).resolve(ast); + + new CompoundAssignDesugarer(this.astCreator).desugar(ast); + + new Checker(this.reporter).check(ast); + + if (this.reporter.errorOccured()) { + console.error("Errors occurred, stopping compilation."); + Deno.exit(1); + } + + const lowerer = new Lowerer(lexer.currentPos()); + lowerer.lower(ast); + lowerer.printProgram(); + const { program, fnNames } = lowerer.finish(); + + return { program, fnNames }; + } +} diff --git a/compiler/lib.ts b/compiler/lib.ts new file mode 100644 index 0000000..566e737 --- /dev/null +++ b/compiler/lib.ts @@ -0,0 +1,6 @@ +import { Compiler, CompileResult } from "./compiler.ts"; + +export async function compileWithDebug(path: string): Promise { + const { program, fnNames } = await new Compiler(path).compile(); + return { program, fnNames }; +} diff --git a/compiler/lowerer.ts b/compiler/lowerer.ts index f0c41da..6feae3f 100644 --- a/compiler/lowerer.ts +++ b/compiler/lowerer.ts @@ -1,15 +1,17 @@ -import { Builtins } from "./arch.ts"; +import { Builtins, Ops } from "./arch.ts"; import { Expr, Stmt } from "./ast.ts"; import { LocalLeaf, Locals, LocalsFnRoot } from "./lowerer_locals.ts"; -import { Ops } from "./mod.ts"; import { Assembler, Label } from "./assembler.ts"; import { vtypeToString } from "./vtype.ts"; import { Pos } from "./token.ts"; +export type FnNamesMap = { [pc: number]: string }; + export class Lowerer { private program = new Assembler(); private locals: Locals = new LocalsFnRoot(); - private fnStmtIdLabelMap: { [key: number]: string } = {}; + private fnStmtIdLabelMap: { [stmtId: number]: string } = {}; + private fnLabelNameMap: { [name: string]: string } = {}; private breakStack: Label[] = []; public constructor(private lastPos: Pos) {} @@ -29,8 +31,15 @@ export class Lowerer { this.program.add(Ops.Pop); } - public finish(): number[] { - return this.program.assemble(); + public finish(): { program: number[]; fnNames: FnNamesMap } { + const { program, locs } = this.program.assemble(); + const fnNames: FnNamesMap = {}; + for (const label in locs) { + if (label in this.fnLabelNameMap) { + fnNames[locs[label]] = this.fnLabelNameMap[label]; + } + } + return { program, fnNames }; } private addSourceMap({ index, line, col }: Pos) { @@ -139,6 +148,7 @@ export class Lowerer { ? "main" : `${stmt.kind.ident}_${stmt.id}`; this.program.setLabel({ label }); + this.fnLabelNameMap[label] = stmt.kind.ident; this.addSourceMap(stmt.pos); const outerLocals = this.locals; diff --git a/compiler/main.ts b/compiler/main.ts index 6cb792b..7a34fdd 100644 --- a/compiler/main.ts +++ b/compiler/main.ts @@ -1,50 +1,5 @@ -import { Ast, AstCreator, File } from "./ast.ts"; -import { stmtToString } from "./ast_visitor.ts"; -import { Checker } from "./checker.ts"; -import { CompoundAssignDesugarer } from "./desugar/compound_assign.ts"; -import { SpecialLoopDesugarer } from "./desugar/special_loop.ts"; -import { Reporter } from "./info.ts"; -import { Lexer } from "./lexer.ts"; -import { Lowerer } from "./lowerer.ts"; -import { Parser } from "./parser.ts"; -import { Resolver } from "./resolver.ts"; +import { Compiler } from "./compiler.ts"; -class Compilation { - private files: File[] = []; - - public constructor(private startFile: string) {} - - public compile(): Ast { - throw new Error(); - } -} - -const text = await Deno.readTextFile(Deno.args[0]); - -const astCreator = new AstCreator(); -const reporter = new Reporter(); - -const lexer = new Lexer(text, reporter); - -const parser = new Parser(lexer, astCreator, reporter); -const ast = parser.parse(); - -new SpecialLoopDesugarer(astCreator).desugar(ast); - -new Resolver(reporter).resolve(ast); - -new CompoundAssignDesugarer(astCreator).desugar(ast); - -new Checker(reporter).check(ast); - -if (reporter.errorOccured()) { - console.error("Errors occurred, stopping compilation."); - Deno.exit(1); -} - -const lowerer = new Lowerer(lexer.currentPos()); -lowerer.lower(ast); -lowerer.printProgram(); -const program = lowerer.finish(); +const { program } = await new Compiler(Deno.args[0]).compile(); await Deno.writeTextFile("out.slgbc", JSON.stringify(program)); diff --git a/compiler/mod.ts b/compiler/mod.ts index 5c1fc23..49f79f5 100644 --- a/compiler/mod.ts +++ b/compiler/mod.ts @@ -1,7 +1,2 @@ -export * from "./parser.ts"; -export * from "./ast.ts"; -export * from "./arch.ts"; -export * from "./lexer.ts"; -export * from "./token.ts"; +export { compileWithDebug } from "./lib.ts"; -export * from "./temputils.ts"; diff --git a/compiler/temputils.ts b/compiler/temputils.ts deleted file mode 100644 index e1dce64..0000000 --- a/compiler/temputils.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Checker } from "./checker.ts"; -import { Reporter } from "./info.ts"; -import { Lexer } from "./lexer.ts"; -import { Lowerer } from "./lowerer.ts"; -import { Parser } from "./parser.ts"; -import { Resolver } from "./resolver.ts"; - -/// TODO: find a better place for this function -export async function compileWithDebug(path: string): Promise { - const text = await Deno.readTextFile(path); - - const reporter = new Reporter(); - - const lexer = new Lexer(text, reporter); - - const parser = new Parser(lexer, reporter); - const ast = parser.parse(); - - new Resolver(reporter).resolve(ast); - new Checker(reporter).check(ast); - - if (reporter.errorOccured()) { - console.error("Errors occurred, stopping compilation."); - } - - const lowerer = new Lowerer(lexer.currentPos()); - lowerer.lower(ast); - lowerer.printProgram(); - const program = lowerer.finish(); - return program; -} diff --git a/slige-build-run-all.sh b/slige-build-run-all.sh new file mode 100755 index 0000000..b1167ee --- /dev/null +++ b/slige-build-run-all.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +set -e + +FILE_FULL_PATH=$(readlink -f $1) + +cd runtime +make +cd .. + +cd web/public +deno task bundle +cd ../.. + +./runtime/build/sliger & + +cd web + +deno run --allow-net --allow-read main.ts $FILE_FULL_PATH + diff --git a/web/deno.lock b/web/deno.lock index 36c9b3b..5b4bb85 100644 --- a/web/deno.lock +++ b/web/deno.lock @@ -13,6 +13,7 @@ "jsr:@std/http@1": "1.0.10", "jsr:@std/io@0.224": "0.224.9", "jsr:@std/media-types@1": "1.1.0", + "jsr:@std/path@*": "1.0.8", "jsr:@std/path@1": "1.0.8", "npm:@types/node@*": "22.5.4", "npm:path-to-regexp@6.2.1": "6.2.1" @@ -39,7 +40,7 @@ "jsr:@std/http", "jsr:@std/io", "jsr:@std/media-types", - "jsr:@std/path", + "jsr:@std/path@1", "npm:path-to-regexp" ] }, diff --git a/web/main.ts b/web/main.ts index 9be0750..8b78de3 100644 --- a/web/main.ts +++ b/web/main.ts @@ -13,37 +13,32 @@ if (flags._.length !== 1) { throw new Error("please specify a filename"); } +// + const filepath = flags._[0] as string; const text = await Deno.readTextFile(filepath); const runtime = new Runtime(13370); -// await runtime.start(); - -async function compileProgram(filepath: string) { - const result = await compiler.compileWithDebug(filepath); - return result; -} async function runProgramWithDebug(program: number[]) { - console.log("connecting"); const connection = await runtime.connect(); - console.log("conneced"); connection.send({ type: "run-debug", program, }); - console.log("sent"); const res = await connection.receive<{ ok: boolean; }>(); - console.log("received"); connection.close(); if (!res.ok) { throw new Error("could not run code"); } } -await compileProgram(filepath).then(runProgramWithDebug); +const { program, fnNames } = await compiler.compileWithDebug(filepath); +await runProgramWithDebug(program); + +// const router = new Router(); @@ -91,6 +86,12 @@ router.get("/api/flame-graph", async (ctx) => { ctx.respond = true; }); +router.get("/api/flame-graph-fn-names", (ctx) => { + ctx.response.body = { ok: true, fnNames }; + ctx.response.status = 200; + ctx.respond = true; +}); + router.get("/api/code-coverage", async (ctx) => { const connection = await runtime.connect(); connection.send({ type: "code-coverage" }); @@ -110,6 +111,8 @@ router.get("/api/code-coverage", async (ctx) => { ctx.respond = true; }); +// + const app = new Application(); app.use(router.routes()); app.use(router.allowedMethods()); diff --git a/web/public/src/data.ts b/web/public/src/data.ts index fcc4ed6..2c79b21 100644 --- a/web/public/src/data.ts +++ b/web/public/src/data.ts @@ -11,6 +11,14 @@ export async function flameGraphData(): Promise { .then((v) => v.flameGraph); } +export type FlameGraphFnNames = { [key: number]: string }; + +export async function flameGraphFnNames(): Promise { + return await fetch("/api/flame-graph-fn-names") + .then((v) => v.json()) + .then((v) => v.fnNames); +} + export async function codeData(): Promise { return await fetch("/api/source") .then((v) => v.json()) diff --git a/web/public/src/index.ts b/web/public/src/index.ts index 2a7ce53..4cb7607 100644 --- a/web/public/src/index.ts +++ b/web/public/src/index.ts @@ -1,4 +1,5 @@ import * as data from "./data.ts"; +import { FlameGraphNode } from "./data.ts"; type Color = { r: number; g: number; b: number }; @@ -130,39 +131,50 @@ function loadCodeCoverage( }); } -type FlameGraphFnNames = { [key: number]: string }; - function loadFlameGraph( flameGraphData: data.FlameGraphNode, - fnNames: FlameGraphFnNames, + fnNames: data.FlameGraphFnNames, flameGraphDiv: HTMLDivElement, ) { flameGraphDiv.innerHTML = ` - - +
+
+ + +
+
+
+ +
`; const canvas = document.querySelector( "#flame-graph-canvas", )!; + const resetButton = document.querySelector( + "#flame-graph-reset", + )!; + canvas.width = 1000; canvas.height = 500; - const ctx = canvas.getContext("2d")!; - ctx.font = "16px monospace"; + const fnNameFont = "600 14px monospace"; - type Node = { + const ctx = canvas.getContext("2d")!; + ctx.font = fnNameFont; + + type CalcNode = { x: number; y: number; w: number; h: number; title: string; percent: string; + fgNode: FlameGraphNode; }; - const nodes: Node[] = []; - function calculateNodeRects( + nodes: CalcNode[], node: data.FlameGraphNode, depth: number, totalAcc: data.FlameGraphNode["acc"], @@ -173,9 +185,11 @@ function loadFlameGraph( const w = ((node.acc + 1) / totalAcc) * canvas.width; const h = 30; - const title = fnNames[node.fn]; + const title = node.fn == 0 + ? "" + : fnNames[node.fn] ?? ""; const percent = `${(node.acc / totalAcc * 100).toFixed(1)}%`; - nodes.push({ x, y, w, h, title, percent }); + nodes.push({ x, y, w, h, title, percent, fgNode: node }); const totalChildrenAcc = node.children.reduce( (acc, child) => acc + child.acc, @@ -183,51 +197,102 @@ function loadFlameGraph( ); let newOffsetAcc = offsetAcc + (node.acc - totalChildrenAcc) / 2; for (const child of node.children) { - calculateNodeRects(child, depth + 1, totalAcc, newOffsetAcc); + calculateNodeRects(nodes, 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( + function drawTextCanvas(node: CalcNode): HTMLCanvasElement { + const { w, h, title } = node; + const textCanvas = document.createElement("canvas"); + textCanvas.width = Math.max(w - 8, 1); + textCanvas.height = h; + const textCtx = textCanvas.getContext("2d")!; + textCtx.font = fnNameFont; + textCtx.fillStyle = "black"; + textCtx.fillText( title, - (x + (w - 10) / 2 - ctx.measureText(title).width / 2) + 5, - y + 20, + ((w - 10) / 2 - ctx.measureText(title).width / 2) + 5 - 4, + 20, ); + return textCanvas; } - const tooltip = document.getElementById("flame-graph-tooltip")!; - - canvas.addEventListener("mousemove", (e) => { - const x = e.offsetX; - const y = e.offsetY; - const node = nodes.find((node) => - x >= node.x && x < node.x + node.w && y >= node.y && - y < node.y + node.h - ); - - if (!node) { - tooltip.hidden = true; - return; + function renderNodes(nodes: CalcNode[]) { + for (const node of nodes) { + const { x, y, w, h } = node; + ctx.fillStyle = "rgb(255, 125, 0)"; + ctx.fillRect( + x + 2, + y + 2, + w - 4, + h - 4, + ); + const textCanvas = drawTextCanvas(node); + ctx.drawImage(textCanvas, x + 4, y); } - 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; + const tooltip = document.getElementById("flame-graph-tooltip")!; + + const mousemoveEvent = (e: MouseEvent) => { + const x = e.offsetX; + const y = e.offsetY; + const node = nodes.find((node) => + x >= node.x && x < node.x + node.w && y >= node.y && + y < node.y + node.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; + }; + const mouseleaveEvent = () => { + tooltip.hidden = true; + }; + const mousedownEvent = (e: MouseEvent) => { + const x = e.offsetX; + const y = e.offsetY; + const node = nodes.find((node) => + x >= node.x && x < node.x + node.w && y >= node.y && + y < node.y + node.h + ); + if (!node) { + return; + } + tooltip.hidden = true; + const newNodes: CalcNode[] = []; + calculateNodeRects( + newNodes, + node.fgNode, + 0, + node.fgNode.acc, + 0, + ); + canvas.removeEventListener("mousemove", mousemoveEvent); + canvas.removeEventListener("mouseleave", mouseleaveEvent); + canvas.removeEventListener("mousedown", mousedownEvent); + ctx.clearRect(0, 0, canvas.width, canvas.height); + renderNodes(newNodes); + }; + + canvas.addEventListener("mousemove", mousemoveEvent); + canvas.addEventListener("mouseleave", mouseleaveEvent); + canvas.addEventListener("mousedown", mousedownEvent); + } + + resetButton.addEventListener("click", () => { + const nodes: CalcNode[] = []; + calculateNodeRects(nodes, flameGraphData, 0, flameGraphData.acc, 0); + renderNodes(nodes); }); + + const nodes: CalcNode[] = []; + calculateNodeRects(nodes, flameGraphData, 0, flameGraphData.acc, 0); + renderNodes(nodes); } async function main() { @@ -263,22 +328,33 @@ async function main() { const codeData = await data.codeData(); const codeCoverageData = await data.codeCoverageData(); const flameGraphData = await data.flameGraphData(); + const flameGraphFnNames = await data.flameGraphFnNames(); const view = document.querySelector("#view")!; const renderFunctions: RenderFns = { "source-code": () => { - const container = document.createElement("div"); - container.classList.add("code-container"); + const outerContainer = document.createElement("div"); + outerContainer.classList.add("code-container"); + + const innerContainer = document.createElement("div"); + innerContainer.classList.add("code-container-inner"); + const lines = createLineElement(codeData); const code = document.createElement("pre"); + code.classList.add("code-source"); code.innerText = codeData; - container.append(lines, code); - view.replaceChildren(container); + innerContainer.append(lines, code); + outerContainer.append(innerContainer); + view.replaceChildren(outerContainer); }, "code-coverage": () => { - const container = document.createElement("div"); - container.classList.add("code-container"); + const outerContainer = document.createElement("div"); + outerContainer.classList.add("code-container"); + + const innerContainer = document.createElement("div"); + innerContainer.classList.add("code-container-inner"); + const tooltip = document.createElement("div"); tooltip.id = "covers-tooltip"; tooltip.hidden = true; @@ -291,19 +367,18 @@ async function main() { tooltip, ); const lines = createLineElement(codeData); - container.append(lines, code); + innerContainer.append(lines, code); + outerContainer.append(innerContainer); const view = document.querySelector("#view")!; - view.replaceChildren(container, tooltip); + view.replaceChildren(outerContainer, tooltip); }, "flame-graph": () => { const container = document.createElement("div"); + container.classList.add("flame-graph"); + container.id = "flame-graph"; const view = document.querySelector("#view")!; view.replaceChildren(container); - loadFlameGraph(flameGraphData, { - 0: "", - 12: "add", - 18: "main", - }, container); + loadFlameGraph(flameGraphData, flameGraphFnNames, container); }, }; diff --git a/web/public/style.css b/web/public/style.css index 19d099d..1a2e852 100644 --- a/web/public/style.css +++ b/web/public/style.css @@ -6,10 +6,10 @@ --bg-2: #313338; --fg-2: #666666; - --black: #211F1C; - --black-transparent: #211F1Caa; - --white: #ECEBE9; - --white-transparent: #ECEBE9aa; + --black: #211f1c; + --black-transparent: #211f1caa; + --white: #ecebe9; + --white-transparent: #ecebe9aa; --code-status: var(--white); } @@ -25,15 +25,15 @@ body { } body.status-error { - --code-status: #FF595E; + --code-status: #ff595e; } body.status-waiting { - --code-status: #E3B23C; + --code-status: #e3b23c; } body.status-done { - --code-status: #63A46C; + --code-status: #63a46c; } main { @@ -72,10 +72,11 @@ main #cover { #views-nav { display: flex; flex-direction: column; - padding: 1rem; + padding: 1rem 0; border-radius: 0.25rem; border: 2px solid var(--code-status); gap: 0.5rem; + min-width: 200px; } #views-nav input { @@ -87,40 +88,62 @@ main #cover { padding: 0.4em; padding-bottom: 0.2em; cursor: pointer; + width: 100%; + text-align: center; +} + +#views-nav label:hover { + background-color: rgba(255, 255, 255, 0.2); +} + +#view { + overflow: hidden; } #view .code-container { + max-height: 100%; + overflow: scroll; + background-color: rgba(255, 255, 255, 0.1); + padding: 0.5rem; + border-radius: 0.5rem; +} + +#view .code-container-inner { display: flex; font-size: 1rem; + overflow: scroll; + max-height: 100%; } #view .code-lines { - background-color: rgba(255, 255, 255, 0.1); color: var(--white-transparent); border-right: 1px solid currentcolor; - padding: 0.5rem; - border-radius: 0.5rem 0 0 0.5rem; + padding-right: 0.5rem; + margin: 0; } #view .code-source { - background-color: rgba(255, 255, 255, 0.1); - padding: 0.5rem; - border-radius: 0 0.5rem 0.5rem 0; + width: 100%; + padding-left: 0.5rem; + margin: 0; } +#view-nav div { + width: 100%; +} #views-nav input:checked + label { background-color: var(--code-status); color: var(--black); } - #views-layout { display: flex; margin: 0 auto; padding: 1rem; gap: 1rem; max-width: 1500px; + height: calc(100vh - 100px); } #covers-tooltip { @@ -137,11 +160,21 @@ main #cover { } #flame-graph { + width: min-content; +} + +#flame-graph #fg-background { + background-color: rgba(255, 255, 255, 0.1); + padding: 0.5rem; + border-radius: 0.5rem; + width: min-content; +} + +#flame-graph #canvas-div { width: 1004px; height: 504px; - background-color: var(--bg-2); - border: 2px solid rgb(240, 220, 200); - padding: 2px; + /*border: 2px solid rgb(240, 220, 200);*/ + padding: 4px; } #flame-graph canvas { z-index: 1; @@ -151,6 +184,17 @@ main #cover { image-rendering: pixelated; transform: translate(-2px, -2px); } +#flame-graph #toolbar { + margin: 20px; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; +} +#flame-graph #toolbar button { + padding: 5px 20px; + min-width: 100px; +} #flame-graph #flame-graph-tooltip { z-index: 2; position: fixed; diff --git a/web/runtime.ts b/web/runtime.ts index bf47d2c..2970d7d 100644 --- a/web/runtime.ts +++ b/web/runtime.ts @@ -1,49 +1,7 @@ export class Runtime { - private runtimeProcess?: Deno.ChildProcess; - constructor(private port: number) {} - async checkRuntimeRev() { - const currentRev = new TextDecoder().decode( - await new Deno.Command("git", { args: ["rev-parse", "HEAD"] }) - .output() - .then((output) => output.stdout), - ).trim(); - const runtimeRev = (await Deno.readTextFile("../runtime/build/rev")) - .trim(); - if (runtimeRev !== currentRev) { - console.error( - "runtime out-of-date; run 'make' inside runtime/ folder", - ); - Deno.exit(1); - } - } - - async start() { - await this.checkRuntimeRev(); - this.runtimeProcess = new Deno.Command("../runtime/build/sliger", { - args: [], - stdout: "piped", - }).spawn(); - } - - stop() { - this.runtimeProcess?.kill(); - this.runtimeProcess = undefined; - } - async connect(): Promise { - // return await new Promise((resolve) => { - // setTimeout(async () => { - // resolve( - // new RuntimeConnection( - // await Deno.connect({ - // port: this.port, - // }), - // ), - // ); - // }, 100); - // }); return new RuntimeConnection( await Deno.connect({ port: this.port,