tolkfmt-test-dev
Version:
Code formatter for the Tolk programming language
171 lines • 5.76 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.render = render;
const doc_1 = require("./doc");
const doc_2 = require("./doc");
function render(doc, printWidth) {
const out = [];
const lineSuffix = [];
const stack = [{ doc, mode: "flat", indent: 0 }];
function fits(d, w) {
const fitStack = [d];
let width = w;
while (width >= 0 && fitStack.length > 0) {
const cur = fitStack.pop() ?? (0, doc_2.empty)();
switch (cur.$) {
case "Text": {
width -= cur.value.length;
break;
}
case "Line": {
width -= 1;
break;
}
case "SoftLine": {
break;
}
case "HardLine": {
return false;
}
case "Concat": {
for (let i = cur.parts.length - 1; i >= 0; i--) {
fitStack.push(cur.parts[i]);
}
break;
}
case "Indent": {
fitStack.push(cur.content);
break;
}
case "Group": {
fitStack.push(cur.content);
break;
}
case "LineSuffix": {
fitStack.push(cur.suffix);
break;
}
case "BreakParent": {
return false;
}
case "IfBreak": {
if (cur.flatContent) {
fitStack.push(cur.flatContent);
}
break;
}
case "Empty": {
break;
}
}
}
return width >= 0;
}
// main loop
while (stack.length > 0) {
const { doc: cur, mode, indent } = stack.pop() ?? { doc: (0, doc_2.empty)(), mode: "break", indent: 0 };
switch (cur.$) {
case "Text": {
if (cur.value === "\n") {
const prev = out.at(-1);
// eslint-disable-next-line @typescript-eslint/no-misused-spread
if (prev?.[0] === " " && ![...prev].some(it => it !== " ")) {
out[out.length - 1] = cur.value;
break;
}
}
out.push(cur.value);
break;
}
case "Line": {
if (mode === "flat") {
out.push(" ");
}
else {
flushLineSuffix();
if (indent !== 0) {
stack.push({ doc: (0, doc_1.text)(" ".repeat(indent)), mode: "flat", indent: 0 });
}
stack.push({ doc: (0, doc_1.text)("\n"), mode: "flat", indent: 0 });
}
break;
}
case "SoftLine": {
if (mode !== "flat") {
flushLineSuffix();
if (indent !== 0) {
stack.push({ doc: (0, doc_1.text)(" ".repeat(indent)), mode: "flat", indent: 0 });
}
stack.push({ doc: (0, doc_1.text)("\n"), mode: "flat", indent: 0 });
}
break;
}
case "HardLine": {
flushLineSuffix();
if (indent !== 0) {
stack.push({ doc: (0, doc_1.text)(" ".repeat(indent)), mode: "flat", indent: 0 });
}
stack.push({ doc: (0, doc_1.text)("\n"), mode: "flat", indent: 0 });
break;
}
case "Concat": {
for (let i = cur.parts.length - 1; i >= 0; i--) {
stack.push({ doc: cur.parts[i], mode, indent });
}
break;
}
case "Indent": {
stack.push({ doc: cur.content, mode, indent: indent + cur.indent });
break;
}
case "Group": {
const shouldFlat = fits(cur.content, printWidth - currentColumn(out));
stack.push({ doc: cur.content, mode: shouldFlat ? "flat" : "break", indent });
break;
}
case "LineSuffix": {
lineSuffix.push(cur.suffix);
break;
}
case "BreakParent": {
flushLineSuffix();
break;
}
case "IfBreak": {
stack.push({
doc: mode === "break"
? (cur.breakContent ?? (0, doc_2.empty)())
: (cur.flatContent ?? (0, doc_2.empty)()),
mode,
indent,
});
break;
}
case "Empty": {
break;
}
}
}
return out.join("");
function flushLineSuffix() {
while (lineSuffix.length > 0) {
const suffix = lineSuffix.shift();
if (suffix) {
stack.push({ doc: suffix, mode: "flat", indent: 0 });
}
}
}
}
function currentColumn(buf) {
let col = 0;
for (let i = buf.length - 1; i >= 0; i--) {
const piece = buf[i];
const nl = piece.lastIndexOf("\n");
if (nl !== -1) {
return col + piece.length - nl - 1;
}
col += piece.length;
}
return col;
}
//# sourceMappingURL=render.js.map