UNPKG

utf-railroad

Version:
364 lines 14.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.terminal = exports.stack = exports.special = exports.skip = exports.sequence = exports.repeater = exports.optional = exports.nonTerminal = exports.horizontalChoice = exports.group = exports.draw = exports.diagram = exports.commentWithLine = exports.comment = exports.choice = void 0; const drawLine = (plotter, { x, y }, text) => [...text].forEach((char, i) => plotter(x + i, y, char)); const delta = ({ x, y }, xDelta = 0, yDelta = 0) => ({ x: x + xDelta, y: y + yDelta, }); const s = { h: "─", hu: "┴", hd: "┬", v: "│", vr: "├", vl: "┤", tl: "╭", tr: "╮", bl: "╰", br: "╯", right: "→", left: "←", }; const terminal = (name) => ({ up: 1, down: 1, height: 0, width: name.length + 4, draw: (plotter, start) => { drawLine(plotter, delta(start, 0, -1), `╭${"─".repeat(name.length + 2)}╮`); drawLine(plotter, start, `┤ ${name} ├`); drawLine(plotter, delta(start, 0, 1), `╰${"─".repeat(name.length + 2)}╯`); }, }); exports.terminal = terminal; const nonTerminal = (name) => ({ up: 1, down: 1, height: 0, width: name.length + 4, draw: (plotter, start) => { drawLine(plotter, delta(start, 0, -1), `┏${"━".repeat(name.length + 2)}┓`); drawLine(plotter, start, `┨ ${name} ┠`); drawLine(plotter, delta(start, 0, 1), `┗${"━".repeat(name.length + 2)}┛`); }, }); exports.nonTerminal = nonTerminal; const special = (name) => ({ up: 1, down: 1, height: 0, width: name.length + 4, draw: (plotter, start) => { drawLine(plotter, delta(start, 0, -1), `╭${"┄".repeat(name.length + 2)}╮`); drawLine(plotter, start, `┤ ${name} ├`); drawLine(plotter, delta(start, 0, 1), `╰${"┄".repeat(name.length + 2)}╯`); }, }); exports.special = special; const comment = (comment) => ({ up: 0, down: 0, height: 0, width: comment.length + 2, draw: (plotter, start) => { drawLine(plotter, start, `╴${comment}╶`); }, }); exports.comment = comment; const commentWithLine = (comment) => ({ up: 1, down: 0, height: 0, width: comment.length + 2, draw: (plotter, start) => { drawLine(plotter, delta(start, 0, -1), ` ${comment} `); drawLine(plotter, start, "─".repeat(comment.length + 2)); }, }); exports.commentWithLine = commentWithLine; const alignWidth = (plotter, element, { x, y }, width) => { const remainingWidth = width - element.width; const left = Math.floor(remainingWidth / 2); const right = remainingWidth - left; element.draw(plotter, { x: x + left, y }); for (let l = 0; l < left; l++) { plotter(x + l, y, s.h); } for (let r = 0; r < right; r++) { plotter(x + left + element.width + r, y + element.height, s.h); } }; const group = (element, label = "") => { const width = Math.max(element.width, label.length + 2); return { up: element.up + 1, down: element.down + 1, height: element.height, width: width + 2, draw: (plotter, start) => { plotter(start.x, start.y - element.up - 1, s.tl); if (label.length > 0) { comment(label).draw(plotter, delta(start, 1, -1 - element.up)); } drawLine(plotter, delta(start, 1, -1 - element.up), `${"╌".repeat(width)}╮`); for (let y = 1; y <= element.up; y++) { plotter(start.x, start.y - y, "╎"); } plotter(start.x, start.y, "┼"); for (let y = 1; y <= element.down + element.height; y++) { plotter(start.x, start.y + y, "╎"); } alignWidth(plotter, element, delta(start, 1, 0), width); for (let y = 1; y <= element.up + element.height; y++) { plotter(start.x + width + 1, start.y + element.height - y, "╎"); } plotter(start.x + width + 1, start.y + element.height, "┼"); for (let y = 1; y <= element.down; y++) { plotter(start.x + width + 1, start.y + element.height + y, "╎"); } drawLine(plotter, delta(start, 0, 1 + element.down + element.height), `╰${"╌".repeat(width)}╯`); }, }; }; exports.group = group; const diagram = (element, complex = false) => ({ up: 0 + element.up, down: 0 + element.down + element.height, height: element.height, width: 2 + element.width, draw: (plotter, start) => { plotter(start.x, start.y, complex ? "╟" : "┠"); plotter(start.x + element.width + 1, start.y + element.height, complex ? "╢" : "┨"); element.draw(plotter, delta(start, 1)); }, }); exports.diagram = diagram; const sequence = (elements) => ({ items: elements, up: Math.max(...elements.map((e) => e.up)), down: Math.max(...elements.map((e) => e.down)), height: 0, width: elements.reduce((acc, element) => acc + element.width + 1, 1), draw: (plotter, start) => { plotter(start.x, start.y, s.h); let xDelta = 1; elements.forEach((element) => { element.draw(plotter, delta(start, xDelta)); xDelta += element.width; if (element.height === 0) { plotter(start.x + xDelta, start.y, s.h); } else { plotter(start.x + xDelta, start.y, s.tl); for (let h = 1; h < element.height; h++) { plotter(start.x + xDelta, start.y + h, s.v); } plotter(start.x + xDelta, start.y + element.height, s.br); } xDelta += 1; }); }, }); exports.sequence = sequence; const skip = (content = "") => ({ up: 0, down: 0, height: 0, width: content.length, draw: (plotter, start) => { drawLine(plotter, start, content); }, }); exports.skip = skip; const choice = (elements, defaultChoice = 0) => { if (elements.length === 1) { return elements[0]; } const up = elements.reduce((acc, element, i) => i === defaultChoice ? acc + element.up : i < defaultChoice ? acc + element.up + element.down + element.height + 1 : acc, 0); const down = elements.reduce((acc, element, i) => i === defaultChoice ? acc + element.down + element.height : i > defaultChoice ? acc + element.up + element.down + element.height + 1 : acc, 0); const width = Math.max(...elements.map((e) => e.width)); const height = elements[defaultChoice].height; return { items: elements, up, down: down - height, height, width: width + 2, draw: (plotter, start) => { let yDelta = -up; elements.forEach((element, i, l) => { if (i === 0) { plotter(start.x, start.y + yDelta + element.up, yDelta < -element.up ? s.tl : s.hd); plotter(start.x + width + 1, start.y + yDelta + element.height + element.up, yDelta < -element.up ? s.tr : s.hd); } else if (i === l.length - 1) { plotter(start.x, start.y + yDelta + element.up, yDelta > 0 ? s.bl : s.hu); plotter(start.x + width + 1, start.y + yDelta + element.height + element.up, yDelta > 0 ? s.br : s.hu); } else { plotter(start.x, start.y + yDelta + element.up, i === defaultChoice ? "┼" : "├"); plotter(start.x + width + 1, start.y + yDelta + element.height + element.up, i === defaultChoice ? "┼" : "┤"); } if (i !== l.length - 1) { for (let y = 1; y <= element.down + element.height; y++) { plotter(start.x, start.y + element.up + yDelta + y, s.v); } for (let y = 1; y <= element.down; y++) { plotter(start.x + 1 + width, start.y + element.up + yDelta + y + element.height, s.v); } } if (i !== 0) { for (let y = 1; y <= element.up; y++) { plotter(start.x, start.y + element.up + yDelta - y, s.v); } for (let y = 1; y <= element.up + element.height; y++) { plotter(start.x + 1 + width, start.y + element.up + yDelta - y + element.height, s.v); } } alignWidth(plotter, element, delta(start, 1, yDelta + element.up), width); yDelta += element.height + element.down + +element.up + 1; }); }, }; }; exports.choice = choice; const optional = (element) => choice([skip(s.right), element], 1); exports.optional = optional; const stack = (elements) => { const up = elements[0].up; const down = elements[elements.length - 1].down; const height = elements.reduce((acc, element, i, l) => i === 0 ? acc + element.height + 1 : acc + l[i - 1].down + element.up + element.height + 1, elements.length - 2); const width = Math.max(...elements.map((e) => e.width)); return { items: elements, up, down, height, width: width + 2, draw: (plotter, start) => { plotter(start.x, start.y, s.h); let yDelta = -up; elements.forEach((element, i, l) => { alignWidth(plotter, element, delta(start, 1, yDelta + element.up), width); if (i < l.length - 1) { alignWidth(plotter, skip(s.left), delta(start, 1, yDelta + element.up + element.height + element.down + 1), width); plotter(start.x + width + 1, start.y + element.up + element.height + yDelta, s.tr); for (let y = 1; y <= element.down; y++) { plotter(start.x + width + 1, start.y + element.up + element.height + yDelta + y, s.v); } plotter(start.x + width + 1, start.y + element.up + element.height + yDelta + element.down + 1, s.br); } if (i > 0) { plotter(start.x, start.y + yDelta - 1, s.tl); for (let y = 0; y < element.up; y++) { plotter(start.x, start.y + yDelta + y, s.v); } plotter(start.x, start.y + yDelta + element.up, s.bl); } yDelta += element.up + element.height + 2 + element.down; }); plotter(start.x + width + 1, start.y + height, s.h); }, }; }; exports.stack = stack; const repeater = (repeat, inBetween = skip()) => choice([repeat, sequence([inBetween, skip(s.left)])]); exports.repeater = repeater; const horizontalChoice = (elements) => { if (elements.length === 1) { return elements[0]; } const up = elements.reduce((acc, element) => (element.up > acc ? element.up : acc), -Infinity); const down = elements.reduce((acc, element) => element.down + element.height > acc ? element.down + element.height : acc, -Infinity); const width = elements.reduce((acc, element) => acc + element.width + 2, 0); return { items: elements, up, down, height: 0, width, draw: (plotter, start) => { plotter(start.x, start.y, s.hu); let xDelta = 1; elements.forEach((element, i) => { for (let y = 1; y <= up; y++) { plotter(start.x + xDelta - 1, start.y - y, s.v); } if (i === 0) { plotter(start.x + xDelta - 1, start.y - up - 1, s.tl); drawLine(plotter, delta(start, xDelta, -up - 1), s.h.repeat(element.width + 1)); } else if (i === elements.length - 1) { plotter(start.x + xDelta - 1, start.y - up - 1, s.tr); drawLine(plotter, delta(start, xDelta - 1, down + 1), s.h.repeat(element.width + 1)); } else { plotter(start.x + xDelta - 1, start.y - up - 1, s.hd); drawLine(plotter, delta(start, xDelta, -up - 1), s.h.repeat(element.width + 1)); drawLine(plotter, delta(start, xDelta - 1, down + 1), s.h.repeat(element.width + 1)); } element.draw(plotter, delta(start, xDelta)); xDelta += element.width; for (let y = 1; y <= down - element.height; y++) { plotter(start.x + xDelta, start.y + y + element.height, s.v); } if (i === 0) { plotter(start.x + xDelta, start.y + down + 1, s.bl); } else if (i === elements.length - 1) { plotter(start.x + xDelta, start.y + down + 1, s.br); } else { plotter(start.x + xDelta, start.y + down + 1, s.hu); } if (i < elements.length - 1) { plotter(start.x + xDelta, start.y + element.height, s.tr); plotter(start.x + xDelta + 1, start.y, s.bl); } else { plotter(start.x + xDelta, start.y, s.hd); } xDelta += 2; }); }, }; }; exports.horizontalChoice = horizontalChoice; const draw = (element) => { const points = []; const plotter = (x, y, char) => { points.push({ x, y, char }); }; element.draw(plotter, { x: 0, y: 0 }); const minX = points.reduce((min, item) => (item.x < min ? item.x : min), Infinity); const minY = points.reduce((min, item) => (item.y < min ? item.y : min), Infinity); const maxX = points.reduce((max, item) => (item.x > max ? item.x : max), -Infinity); const maxY = points.reduce((max, item) => (item.y > max ? item.y : max), -Infinity); const height = maxY - minY; const width = maxX - minX; const canvas = Array(height + 1) .fill(null) .map(() => Array(width + 1).fill(" ")); for (let y = minY; y <= maxY; y++) { for (let x = minX; x <= maxX; x++) { const point = points.find((e) => e.x === x && e.y === y); if (point) { canvas[y - minY][x - minX] = point.char; } } } return canvas.map((l) => l.join("")).join("\n"); }; exports.draw = draw; //# sourceMappingURL=utf-railroad.js.map