@blainehansen/macro-ts
Version:
An ergonomic typescript compiler that enables typesafe syntactic macros.
170 lines • 7.14 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.formatDiagnostic = exports.formatDiagnostics = exports.SpanResult = exports.SpanWarning = exports.SpanError = exports.Span = void 0;
const chalk = require("chalk");
const monads_1 = require("@ts-std/monads");
function Span(file, start, text, line, column) {
return { file, start, end: start + text.length, line, column };
}
exports.Span = Span;
(function (Span) {
function fromTsNode(sourceFile, { pos: start, end }) {
const { line: zeroLine, character: column } = sourceFile.getLineAndCharacterOfPosition(start);
const { text: source, fileName: filename } = sourceFile;
return { file: { source, filename }, start, end, line: zeroLine + 1, column };
}
Span.fromTsNode = fromTsNode;
})(Span = exports.Span || (exports.Span = {}));
function SpanError(region, title, paragraphs) {
return { region, title, message: paragraphs.join("\n"), error: true };
}
exports.SpanError = SpanError;
function SpanWarning(region, title, paragraphs) {
return { region, title, message: paragraphs.join("\n"), error: false };
}
exports.SpanWarning = SpanWarning;
var SpanResult;
(function (SpanResult) {
function Ok(value, warnings) {
return (0, monads_1.Ok)({ value, warnings });
}
SpanResult.Ok = Ok;
function TsNodeErr(sourceFile, node, title, paragraphs) {
return (0, monads_1.Err)({ errors: [SpanError(Span.fromTsNode(sourceFile, node), title, paragraphs)] });
}
SpanResult.TsNodeErr = TsNodeErr;
function Err(fileName, title, paragraphs) {
return (0, monads_1.Err)({ errors: [SpanError(fileName, title, paragraphs)] });
}
SpanResult.Err = Err;
function checkSuccess(errors, warnings) {
return [errors.length ? (0, monads_1.Err)(errors) : (0, monads_1.Ok)(undefined), warnings];
}
SpanResult.checkSuccess = checkSuccess;
class Context {
sourceFile;
errors = [];
warnings = [];
drop() {
const errors = this.errors.splice(0, this.errors.length);
const warnings = this.warnings.splice(0, this.warnings.length);
return { errors, warnings };
}
constructor(sourceFile) {
this.sourceFile = sourceFile;
}
subsume(result) {
if (result.is_ok()) {
const { value, warnings } = result.value;
if (warnings && warnings.length)
this.warnings = this.warnings.concat(warnings);
return (0, monads_1.Ok)(value);
}
const { errors, warnings } = result.error;
if (warnings && warnings.length)
this.warnings = this.warnings.concat(warnings);
this.errors = this.errors.concat(errors);
return (0, monads_1.Err)(undefined);
}
tsNodeWarn(node, title, paragraphs) {
this.warnings.push(SpanWarning(Span.fromTsNode(this.sourceFile, node), title, paragraphs));
}
warn(fileName, title, paragraphs) {
this.warnings.push(SpanWarning(fileName, title, paragraphs));
}
Err(node, title, ...paragraphs) {
this.errors.push(SpanError(Span.fromTsNode(this.sourceFile, node), title, paragraphs));
return (0, monads_1.Err)(undefined);
}
}
SpanResult.Context = Context;
})(SpanResult = exports.SpanResult || (exports.SpanResult = {}));
function formatDiagnostics(errors, warnings, lineWidth) {
return errors.concat(warnings)
.map(d => formatDiagnostic(d, lineWidth))
.join("\n\n\n") + "\n";
}
exports.formatDiagnostics = formatDiagnostics;
const info = chalk.blue.bold;
const file = chalk.magentaBright.bold;
function clean(s) {
return s.replace(/\t/g, " ");
}
function formatDiagnostic({ region, title, message, error }, lineWidth) {
const header = info(`-- ${title} ` + "-".repeat(lineWidth - (title.length + 4)));
if (typeof region === "string") {
const fileHeader = "\n" + " ".repeat(3) + file(region);
return header + "\n" + fileHeader + formatMessage(message, "\n ", lineWidth);
}
const { file: { source, filename }, start, end, line, column } = region;
const highlight = error ? chalk.red.bold : chalk.yellow.bold;
const lineNumberWidth = line.toString().length;
function makeGutter(lineNumber, error = false) {
const insert = lineNumber !== undefined
? " ".repeat(lineNumberWidth - lineNumber.toString().length) + info(lineNumber)
: " ".repeat(lineNumberWidth);
return `\n ${insert} ${info("|")}${error ? highlight(">") : " "} `;
}
const blankGutter = makeGutter();
const margin = `\n${" ".repeat(lineNumberWidth)} `;
const messageLine = "\n" + formatMessage(message, margin, lineWidth);
const fileHeader = (filename ? "\n" + " ".repeat(lineNumberWidth + 3) + file(filename) : "");
let sourceLineStart = start;
for (; sourceLineStart >= 0; sourceLineStart--)
if (source[sourceLineStart] === "\n")
break;
const lines = [];
let currentLineNumber = line;
let currentLineStart = sourceLineStart;
while (currentLineStart < end) {
let currentLineEnd = source.indexOf("\n", currentLineStart + 1);
if (currentLineEnd < 0)
break;
lines.push([currentLineNumber, source.slice(currentLineStart + 1, currentLineEnd)]);
currentLineStart = currentLineEnd;
currentLineNumber++;
}
switch (lines.length) {
case 0: throw new Error("zuh?");
case 1:
const [[, sourceLine]] = lines;
const printSourceLine = clean(sourceLine);
const pointerPrefix = " ".repeat(clean(sourceLine.slice(0, column)).length);
const pointerWidth = end - start;
const pointer = pointerPrefix + highlight("^".repeat(pointerWidth));
return header
+ "\n" + fileHeader
+ blankGutter
+ makeGutter(line) + printSourceLine
+ blankGutter + pointer
+ messageLine;
default:
return header
+ "\n" + fileHeader
+ blankGutter
+ lines.map(([lineNumber, line]) => makeGutter(lineNumber, true) + clean(line)).join("")
+ blankGutter
+ messageLine;
}
}
exports.formatDiagnostic = formatDiagnostic;
function formatMessage(message, margin, lineWidth) {
const finalLineWidth = Math.min(lineWidth, 80);
return message.split(/\n+/).map(paragraph => {
const lines = [];
let line = margin;
const words = paragraph.split(/[ \t]+/);
for (const word of words) {
if (line.length + word.length + 1 > finalLineWidth) {
lines.push(line);
line = margin + " " + word;
}
else
line += " " + word;
}
if (line !== margin)
lines.push(line);
return lines.join("");
}).join("\n");
}
//# sourceMappingURL=message.js.map