import { Ctx, File } from "./ctx.ts"; import { exhausted } from "./util.ts"; export type Span = { begin: Pos; end: Pos; }; export type Pos = { idx: number; line: number; col: number; }; export const Span = { fromto: ({ begin }: Span, { end }: Span): Span => ({ begin, end }), } as const; export type Report = { severity: "fatal" | "error" | "warning" | "info"; origin?: string; msg: string; file?: File; span?: Span; pos?: Pos; }; function severityColor(severity: "fatal" | "error" | "warning" | "info") { switch (severity) { case "fatal": return "\x1b[1m\x1b[31m"; case "error": return "\x1b[1m\x1b[31m"; case "warning": return "\x1b[1m\x1b[33m"; case "info": return "\x1b[1m\x1b[34m"; } exhausted(severity); } export function prettyPrintReport(ctx: Ctx, rep: Report) { const { severity, msg } = rep; const origin = rep.origin ? `\x1b[1m${rep.origin}:\x1b[0m ` : ""; console.error( `${origin}${ severityColor(severity) }${severity}:\x1b[0m \x1b[37m${msg}\x1b[0m`, ); if (rep.file && (rep.span || rep.pos)) { const errorLineOffset = 2; const { absPath: path } = ctx.fileInfo(rep.file); const { line, col } = rep.span?.begin ?? rep.pos!; console.error(` --> ./${path}:${line}:${col}`); if (rep.span) { const spanLines = ctx.fileSpanText(rep.file, rep.span).split("\n"); spanLines.pop(); if (spanLines.length == 1) { console.error( `${rep.span.begin.line.toString().padStart(4, " ")}| ${ spanLines[0] }`, ); console.error( ` | ${severityColor(severity)}${ " ".repeat(rep.span.begin.col) }${ "~".repeat(rep.span.end.col - rep.span.begin.col) }\x1b[0m`, ); return; } for (let i = 0; i < spanLines.length; i++) { console.error( `${ (rep.span.begin.line + i).toString().padStart(4, " ") }| ${spanLines[i]}`, ); if (i == 0) { console.error( ` | ${" ".repeat(rep.span.begin.col - 1)}${ severityColor(severity) }${ "~".repeat( spanLines[i].length - (rep.span.begin.col - 1), ) }\x1b[0m`, ); } else if (i == spanLines.length - 1) { console.error( ` | ${severityColor(severity)}${ "~".repeat(rep.span.end.col) }\x1b[0m`, ); } else { console.error( ` | ${severityColor(severity)}${ "~".repeat(spanLines[i].length) }\x1b[0m`, ); } } } else if (rep.pos) { console.error( `${rep.pos.line.toString().padStart(4, " ")}| ${ ctx.filePosLineText(rep.file, rep.pos) }`, ); console.error( ` | ${severityColor(severity)}${ " ".repeat(rep.pos.col) }^\x1b[0m`, ); } } } export function printStackTrace() { class StackTracer extends Error { constructor() { super("StackTracer"); } } try { throw new StackTracer(); } catch (error) { if (!(error instanceof StackTracer)) { throw error; } console.log( error.stack?.replace( "Error: StackTracer", "Stack trace:", ) ?? error, ); } }