2017-01-10 20:18:22 +03:00
|
|
|
"use strict";
|
2017-01-28 18:50:22 +03:00
|
|
|
|
2018-07-02 05:47:55 +03:00
|
|
|
const { getStringWidth } = require("../common/util");
|
2018-05-27 14:36:12 +03:00
|
|
|
const { concat, fill, cursor } = require("./doc-builders");
|
2017-05-11 02:13:21 +03:00
|
|
|
|
2018-06-21 05:29:42 +03:00
|
|
|
/** @type {{[groupId: PropertyKey]: MODE}} */
|
|
|
|
let groupModeMap;
|
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
const MODE_BREAK = 1;
|
|
|
|
const MODE_FLAT = 2;
|
|
|
|
|
2017-04-07 05:49:37 +03:00
|
|
|
function rootIndent() {
|
2018-01-26 05:02:42 +03:00
|
|
|
return { value: "", length: 0, queue: [] };
|
2017-04-07 05:49:37 +03:00
|
|
|
}
|
|
|
|
|
2017-12-05 00:59:27 +03:00
|
|
|
function makeIndent(ind, options) {
|
2018-01-26 05:02:42 +03:00
|
|
|
return generateInd(ind, { type: "indent" }, options);
|
2017-04-07 05:49:37 +03:00
|
|
|
}
|
|
|
|
|
2017-12-05 00:59:27 +03:00
|
|
|
function makeAlign(ind, n, options) {
|
|
|
|
return n === -Infinity
|
2018-01-26 05:02:42 +03:00
|
|
|
? ind.root || rootIndent()
|
|
|
|
: n < 0
|
|
|
|
? generateInd(ind, { type: "dedent" }, options)
|
|
|
|
: !n
|
|
|
|
? ind
|
|
|
|
: n.type === "root"
|
|
|
|
? Object.assign({}, ind, { root: ind })
|
|
|
|
: typeof n === "string"
|
|
|
|
? generateInd(ind, { type: "stringAlign", n }, options)
|
|
|
|
: generateInd(ind, { type: "numberAlign", n }, options);
|
|
|
|
}
|
|
|
|
|
|
|
|
function generateInd(ind, newPart, options) {
|
|
|
|
const queue =
|
|
|
|
newPart.type === "dedent"
|
|
|
|
? ind.queue.slice(0, -1)
|
|
|
|
: ind.queue.concat(newPart);
|
|
|
|
|
|
|
|
let value = "";
|
|
|
|
let length = 0;
|
|
|
|
let lastTabs = 0;
|
|
|
|
let lastSpaces = 0;
|
|
|
|
|
|
|
|
for (const part of queue) {
|
|
|
|
switch (part.type) {
|
|
|
|
case "indent":
|
|
|
|
flush();
|
|
|
|
if (options.useTabs) {
|
|
|
|
addTabs(1);
|
|
|
|
} else {
|
|
|
|
addSpaces(options.tabWidth);
|
2017-12-05 00:59:27 +03:00
|
|
|
}
|
2018-01-26 05:02:42 +03:00
|
|
|
break;
|
|
|
|
case "stringAlign":
|
|
|
|
flush();
|
|
|
|
value += part.n;
|
|
|
|
length += part.n.length;
|
|
|
|
break;
|
|
|
|
case "numberAlign":
|
|
|
|
lastTabs += 1;
|
|
|
|
lastSpaces += part.n;
|
|
|
|
break;
|
|
|
|
/* istanbul ignore next */
|
|
|
|
default:
|
|
|
|
throw new Error(`Unexpected type '${part.type}'`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
flushSpaces();
|
|
|
|
|
|
|
|
return Object.assign({}, ind, { value, length, queue });
|
|
|
|
|
|
|
|
function addTabs(count) {
|
|
|
|
value += "\t".repeat(count);
|
|
|
|
length += options.tabWidth * count;
|
|
|
|
}
|
|
|
|
|
|
|
|
function addSpaces(count) {
|
|
|
|
value += " ".repeat(count);
|
|
|
|
length += count;
|
|
|
|
}
|
|
|
|
|
|
|
|
function flush() {
|
|
|
|
if (options.useTabs) {
|
|
|
|
flushTabs();
|
|
|
|
} else {
|
|
|
|
flushSpaces();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function flushTabs() {
|
|
|
|
if (lastTabs > 0) {
|
|
|
|
addTabs(lastTabs);
|
|
|
|
}
|
|
|
|
resetLast();
|
|
|
|
}
|
|
|
|
|
|
|
|
function flushSpaces() {
|
|
|
|
if (lastSpaces > 0) {
|
|
|
|
addSpaces(lastSpaces);
|
|
|
|
}
|
|
|
|
resetLast();
|
|
|
|
}
|
|
|
|
|
|
|
|
function resetLast() {
|
|
|
|
lastTabs = 0;
|
|
|
|
lastSpaces = 0;
|
|
|
|
}
|
2017-04-07 05:49:37 +03:00
|
|
|
}
|
|
|
|
|
2018-10-06 15:18:52 +03:00
|
|
|
function trim(out) {
|
|
|
|
if (out.length === 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
let trimCount = 0;
|
|
|
|
|
|
|
|
// Trim whitespace at the end of line
|
|
|
|
while (
|
|
|
|
out.length > 0 &&
|
|
|
|
typeof out[out.length - 1] === "string" &&
|
|
|
|
out[out.length - 1].match(/^[ \t]*$/)
|
|
|
|
) {
|
|
|
|
trimCount += out.pop().length;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (out.length && typeof out[out.length - 1] === "string") {
|
|
|
|
const trimmed = out[out.length - 1].replace(/[ \t]*$/, "");
|
|
|
|
trimCount += out[out.length - 1].length - trimmed.length;
|
|
|
|
out[out.length - 1] = trimmed;
|
|
|
|
}
|
|
|
|
|
|
|
|
return trimCount;
|
|
|
|
}
|
|
|
|
|
2017-12-05 00:59:27 +03:00
|
|
|
function fits(next, restCommands, width, options, mustBeFlat) {
|
2016-12-23 21:38:10 +03:00
|
|
|
let restIdx = restCommands.length;
|
2017-01-28 18:50:22 +03:00
|
|
|
const cmds = [next];
|
2018-10-06 15:18:52 +03:00
|
|
|
// `out` is only used for width counting because `trim` requires to look
|
|
|
|
// backwards for space characters.
|
|
|
|
const out = [];
|
2017-01-05 20:21:57 +03:00
|
|
|
while (width >= 0) {
|
|
|
|
if (cmds.length === 0) {
|
|
|
|
if (restIdx === 0) {
|
2016-12-23 21:38:10 +03:00
|
|
|
return true;
|
2017-06-06 01:12:59 +03:00
|
|
|
}
|
|
|
|
cmds.push(restCommands[restIdx - 1]);
|
2017-01-05 20:21:57 +03:00
|
|
|
|
2017-06-06 01:12:59 +03:00
|
|
|
restIdx--;
|
2017-01-05 20:21:57 +03:00
|
|
|
|
2017-06-06 01:12:59 +03:00
|
|
|
continue;
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
|
|
|
|
2017-01-10 20:18:22 +03:00
|
|
|
const x = cmds.pop();
|
2017-01-19 02:31:46 +03:00
|
|
|
const ind = x[0];
|
|
|
|
const mode = x[1];
|
|
|
|
const doc = x[2];
|
2017-01-05 20:21:57 +03:00
|
|
|
|
|
|
|
if (typeof doc === "string") {
|
2018-10-06 15:18:52 +03:00
|
|
|
out.push(doc);
|
|
|
|
|
2018-07-02 05:47:55 +03:00
|
|
|
width -= getStringWidth(doc);
|
2017-01-05 20:21:57 +03:00
|
|
|
} else {
|
|
|
|
switch (doc.type) {
|
2017-01-13 23:03:53 +03:00
|
|
|
case "concat":
|
2017-05-19 23:00:38 +03:00
|
|
|
for (let i = doc.parts.length - 1; i >= 0; i--) {
|
2017-01-28 18:50:22 +03:00
|
|
|
cmds.push([ind, mode, doc.parts[i]]);
|
2017-01-13 23:03:53 +03:00
|
|
|
}
|
2017-01-05 20:21:57 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
break;
|
|
|
|
case "indent":
|
2017-12-05 00:59:27 +03:00
|
|
|
cmds.push([makeIndent(ind, options), mode, doc.contents]);
|
2017-04-07 05:49:37 +03:00
|
|
|
|
|
|
|
break;
|
|
|
|
case "align":
|
2017-12-05 00:59:27 +03:00
|
|
|
cmds.push([makeAlign(ind, doc.n, options), mode, doc.contents]);
|
2017-01-05 20:21:57 +03:00
|
|
|
|
2018-10-06 15:18:52 +03:00
|
|
|
break;
|
|
|
|
case "trim":
|
|
|
|
width += trim(out);
|
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
break;
|
|
|
|
case "group":
|
2017-05-11 02:13:21 +03:00
|
|
|
if (mustBeFlat && doc.break) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-01-28 18:50:22 +03:00
|
|
|
cmds.push([ind, doc.break ? MODE_BREAK : mode, doc.contents]);
|
2017-01-05 20:21:57 +03:00
|
|
|
|
2018-06-21 05:29:42 +03:00
|
|
|
if (doc.id) {
|
|
|
|
groupModeMap[doc.id] = cmds[cmds.length - 1][1];
|
|
|
|
}
|
2017-05-11 02:13:21 +03:00
|
|
|
break;
|
|
|
|
case "fill":
|
2017-05-19 23:00:38 +03:00
|
|
|
for (let i = doc.parts.length - 1; i >= 0; i--) {
|
2017-05-11 02:13:21 +03:00
|
|
|
cmds.push([ind, mode, doc.parts[i]]);
|
|
|
|
}
|
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
break;
|
2018-06-21 05:29:42 +03:00
|
|
|
case "if-break": {
|
|
|
|
const groupMode = doc.groupId ? groupModeMap[doc.groupId] : mode;
|
|
|
|
if (groupMode === MODE_BREAK) {
|
2017-01-18 23:13:50 +03:00
|
|
|
if (doc.breakContents) {
|
2017-01-28 18:50:22 +03:00
|
|
|
cmds.push([ind, mode, doc.breakContents]);
|
2017-01-18 23:13:50 +03:00
|
|
|
}
|
|
|
|
}
|
2018-06-21 05:29:42 +03:00
|
|
|
if (groupMode === MODE_FLAT) {
|
2017-01-18 23:13:50 +03:00
|
|
|
if (doc.flatContents) {
|
2017-01-28 18:50:22 +03:00
|
|
|
cmds.push([ind, mode, doc.flatContents]);
|
2017-01-18 23:13:50 +03:00
|
|
|
}
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
2017-01-05 20:21:57 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
break;
|
2018-06-21 05:29:42 +03:00
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
case "line":
|
|
|
|
switch (mode) {
|
|
|
|
// fallthrough
|
|
|
|
case MODE_FLAT:
|
|
|
|
if (!doc.hard) {
|
|
|
|
if (!doc.soft) {
|
2018-10-06 15:18:52 +03:00
|
|
|
out.push(" ");
|
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
width -= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2017-05-21 22:11:09 +03:00
|
|
|
return true;
|
2017-01-13 23:03:53 +03:00
|
|
|
|
|
|
|
case MODE_BREAK:
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-04-07 05:49:37 +03:00
|
|
|
function printDocToString(doc, options) {
|
2018-06-21 05:29:42 +03:00
|
|
|
groupModeMap = {};
|
|
|
|
|
2017-05-21 22:11:09 +03:00
|
|
|
const width = options.printWidth;
|
|
|
|
const newLine = options.newLine || "\n";
|
2016-12-23 21:38:10 +03:00
|
|
|
let pos = 0;
|
|
|
|
// cmds is basically a stack. We've turned a recursive call into a
|
|
|
|
// while loop which is much faster. The while loop below adds new
|
|
|
|
// cmds to the array instead of recursively calling `print`.
|
2017-05-21 22:11:09 +03:00
|
|
|
const cmds = [[rootIndent(), MODE_BREAK, doc]];
|
|
|
|
const out = [];
|
2017-01-04 23:24:10 +03:00
|
|
|
let shouldRemeasure = false;
|
2017-02-13 18:13:16 +03:00
|
|
|
let lineSuffix = [];
|
2017-01-26 22:57:43 +03:00
|
|
|
|
2017-01-05 20:21:57 +03:00
|
|
|
while (cmds.length !== 0) {
|
2017-01-10 20:18:22 +03:00
|
|
|
const x = cmds.pop();
|
2017-01-19 02:31:46 +03:00
|
|
|
const ind = x[0];
|
|
|
|
const mode = x[1];
|
|
|
|
const doc = x[2];
|
2016-12-23 21:38:10 +03:00
|
|
|
|
2017-01-05 20:21:57 +03:00
|
|
|
if (typeof doc === "string") {
|
2016-12-23 21:38:10 +03:00
|
|
|
out.push(doc);
|
2017-01-05 20:21:57 +03:00
|
|
|
|
2018-07-02 05:47:55 +03:00
|
|
|
pos += getStringWidth(doc);
|
2017-01-05 20:21:57 +03:00
|
|
|
} else {
|
|
|
|
switch (doc.type) {
|
2017-06-02 01:52:29 +03:00
|
|
|
case "cursor":
|
|
|
|
out.push(cursor.placeholder);
|
|
|
|
|
|
|
|
break;
|
2017-01-13 23:03:53 +03:00
|
|
|
case "concat":
|
2017-05-19 23:00:38 +03:00
|
|
|
for (let i = doc.parts.length - 1; i >= 0; i--) {
|
2017-01-28 18:50:22 +03:00
|
|
|
cmds.push([ind, mode, doc.parts[i]]);
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
2017-01-05 20:21:57 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
break;
|
|
|
|
case "indent":
|
2017-12-05 00:59:27 +03:00
|
|
|
cmds.push([makeIndent(ind, options), mode, doc.contents]);
|
2017-04-07 05:49:37 +03:00
|
|
|
|
|
|
|
break;
|
|
|
|
case "align":
|
2017-12-05 00:59:27 +03:00
|
|
|
cmds.push([makeAlign(ind, doc.n, options), mode, doc.contents]);
|
2017-01-13 23:03:53 +03:00
|
|
|
|
2018-10-06 15:18:52 +03:00
|
|
|
break;
|
|
|
|
case "trim":
|
|
|
|
pos -= trim(out);
|
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
break;
|
|
|
|
case "group":
|
|
|
|
switch (mode) {
|
|
|
|
case MODE_FLAT:
|
|
|
|
if (!shouldRemeasure) {
|
|
|
|
cmds.push([
|
|
|
|
ind,
|
|
|
|
doc.break ? MODE_BREAK : MODE_FLAT,
|
|
|
|
doc.contents
|
|
|
|
]);
|
2017-01-05 20:21:57 +03:00
|
|
|
|
2017-01-04 23:24:10 +03:00
|
|
|
break;
|
2017-01-13 23:03:53 +03:00
|
|
|
}
|
2017-05-21 22:11:09 +03:00
|
|
|
// fallthrough
|
2017-01-13 23:03:53 +03:00
|
|
|
|
2017-05-21 22:11:09 +03:00
|
|
|
case MODE_BREAK: {
|
2017-01-13 23:03:53 +03:00
|
|
|
shouldRemeasure = false;
|
|
|
|
|
2017-01-28 18:50:22 +03:00
|
|
|
const next = [ind, MODE_FLAT, doc.contents];
|
2017-05-21 22:11:09 +03:00
|
|
|
const rem = width - pos;
|
2017-01-13 23:03:53 +03:00
|
|
|
|
2017-12-05 00:59:27 +03:00
|
|
|
if (!doc.break && fits(next, cmds, rem, options)) {
|
2017-01-13 23:03:53 +03:00
|
|
|
cmds.push(next);
|
2017-01-05 20:21:57 +03:00
|
|
|
} else {
|
2017-01-13 23:03:53 +03:00
|
|
|
// Expanded states are a rare case where a document
|
|
|
|
// can manually provide multiple representations of
|
|
|
|
// itself. It provides an array of documents
|
|
|
|
// going from the least expanded (most flattened)
|
|
|
|
// representation first to the most expanded. If a
|
|
|
|
// group has these, we need to manually go through
|
|
|
|
// these states and find the first one that fits.
|
|
|
|
if (doc.expandedStates) {
|
2017-04-12 20:16:11 +03:00
|
|
|
const mostExpanded =
|
|
|
|
doc.expandedStates[doc.expandedStates.length - 1];
|
2017-01-13 23:03:53 +03:00
|
|
|
|
|
|
|
if (doc.break) {
|
2017-01-28 18:50:22 +03:00
|
|
|
cmds.push([ind, MODE_BREAK, mostExpanded]);
|
2017-01-05 20:21:57 +03:00
|
|
|
|
2017-01-04 23:24:10 +03:00
|
|
|
break;
|
2017-01-05 20:21:57 +03:00
|
|
|
} else {
|
2017-05-19 23:00:38 +03:00
|
|
|
for (let i = 1; i < doc.expandedStates.length + 1; i++) {
|
2017-01-13 23:03:53 +03:00
|
|
|
if (i >= doc.expandedStates.length) {
|
2017-01-28 18:50:22 +03:00
|
|
|
cmds.push([ind, MODE_BREAK, mostExpanded]);
|
2017-01-05 20:21:57 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
const state = doc.expandedStates[i];
|
2017-01-28 18:50:22 +03:00
|
|
|
const cmd = [ind, MODE_FLAT, state];
|
2017-01-05 20:21:57 +03:00
|
|
|
|
2017-12-05 00:59:27 +03:00
|
|
|
if (fits(cmd, cmds, rem, options)) {
|
2017-01-13 23:03:53 +03:00
|
|
|
cmds.push(cmd);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2017-01-04 23:24:10 +03:00
|
|
|
}
|
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
} else {
|
2017-01-28 18:50:22 +03:00
|
|
|
cmds.push([ind, MODE_BREAK, doc.contents]);
|
2017-01-04 23:24:10 +03:00
|
|
|
}
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
|
|
|
|
break;
|
2017-05-21 22:11:09 +03:00
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
}
|
2018-06-21 05:29:42 +03:00
|
|
|
|
|
|
|
if (doc.id) {
|
|
|
|
groupModeMap[doc.id] = cmds[cmds.length - 1][1];
|
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
break;
|
2017-05-11 02:13:21 +03:00
|
|
|
// Fills each line with as much code as possible before moving to a new
|
|
|
|
// line with the same indentation.
|
|
|
|
//
|
|
|
|
// Expects doc.parts to be an array of alternating content and
|
|
|
|
// whitespace. The whitespace contains the linebreaks.
|
|
|
|
//
|
|
|
|
// For example:
|
|
|
|
// ["I", line, "love", line, "monkeys"]
|
|
|
|
// or
|
|
|
|
// [{ type: group, ... }, softline, { type: group, ... }]
|
|
|
|
//
|
|
|
|
// It uses this parts structure to handle three main layout cases:
|
|
|
|
// * The first two content items fit on the same line without
|
|
|
|
// breaking
|
|
|
|
// -> output the first content item and the whitespace "flat".
|
|
|
|
// * Only the first content item fits on the line without breaking
|
|
|
|
// -> output the first content item "flat" and the whitespace with
|
|
|
|
// "break".
|
|
|
|
// * Neither content item fits on the line without breaking
|
|
|
|
// -> output the first content item and the whitespace with "break".
|
|
|
|
case "fill": {
|
2017-05-21 22:11:09 +03:00
|
|
|
const rem = width - pos;
|
2017-05-11 02:13:21 +03:00
|
|
|
|
|
|
|
const parts = doc.parts;
|
|
|
|
if (parts.length === 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
const content = parts[0];
|
|
|
|
const contentFlatCmd = [ind, MODE_FLAT, content];
|
|
|
|
const contentBreakCmd = [ind, MODE_BREAK, content];
|
2017-12-05 00:59:27 +03:00
|
|
|
const contentFits = fits(contentFlatCmd, [], rem, options, true);
|
2017-05-11 02:13:21 +03:00
|
|
|
|
|
|
|
if (parts.length === 1) {
|
|
|
|
if (contentFits) {
|
|
|
|
cmds.push(contentFlatCmd);
|
|
|
|
} else {
|
|
|
|
cmds.push(contentBreakCmd);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
const whitespace = parts[1];
|
2017-05-21 21:00:16 +03:00
|
|
|
const whitespaceFlatCmd = [ind, MODE_FLAT, whitespace];
|
|
|
|
const whitespaceBreakCmd = [ind, MODE_BREAK, whitespace];
|
2017-05-11 02:13:21 +03:00
|
|
|
|
|
|
|
if (parts.length === 2) {
|
|
|
|
if (contentFits) {
|
|
|
|
cmds.push(whitespaceFlatCmd);
|
|
|
|
cmds.push(contentFlatCmd);
|
|
|
|
} else {
|
|
|
|
cmds.push(whitespaceBreakCmd);
|
|
|
|
cmds.push(contentBreakCmd);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-11-16 14:55:55 +03:00
|
|
|
// At this point we've handled the first pair (context, separator)
|
|
|
|
// and will create a new fill doc for the rest of the content.
|
|
|
|
// Ideally we wouldn't mutate the array here but coping all the
|
|
|
|
// elements to a new array would make this algorithm quadratic,
|
|
|
|
// which is unusable for large arrays (e.g. large texts in JSX).
|
|
|
|
parts.splice(0, 2);
|
|
|
|
const remainingCmd = [ind, mode, fill(parts)];
|
|
|
|
|
|
|
|
const secondContent = parts[0];
|
2017-05-11 02:13:21 +03:00
|
|
|
|
2017-05-21 21:00:16 +03:00
|
|
|
const firstAndSecondContentFlatCmd = [
|
|
|
|
ind,
|
|
|
|
MODE_FLAT,
|
|
|
|
concat([content, whitespace, secondContent])
|
|
|
|
];
|
|
|
|
const firstAndSecondContentFits = fits(
|
|
|
|
firstAndSecondContentFlatCmd,
|
|
|
|
[],
|
|
|
|
rem,
|
2017-12-05 00:59:27 +03:00
|
|
|
options,
|
2017-05-21 21:00:16 +03:00
|
|
|
true
|
|
|
|
);
|
2017-05-11 02:13:21 +03:00
|
|
|
|
|
|
|
if (firstAndSecondContentFits) {
|
2017-05-21 21:00:16 +03:00
|
|
|
cmds.push(remainingCmd);
|
2017-05-11 02:13:21 +03:00
|
|
|
cmds.push(whitespaceFlatCmd);
|
|
|
|
cmds.push(contentFlatCmd);
|
|
|
|
} else if (contentFits) {
|
2017-05-21 21:00:16 +03:00
|
|
|
cmds.push(remainingCmd);
|
2017-05-11 02:13:21 +03:00
|
|
|
cmds.push(whitespaceBreakCmd);
|
|
|
|
cmds.push(contentFlatCmd);
|
|
|
|
} else {
|
|
|
|
cmds.push(remainingCmd);
|
|
|
|
cmds.push(whitespaceBreakCmd);
|
|
|
|
cmds.push(contentBreakCmd);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2018-06-21 05:29:42 +03:00
|
|
|
case "if-break": {
|
|
|
|
const groupMode = doc.groupId ? groupModeMap[doc.groupId] : mode;
|
|
|
|
if (groupMode === MODE_BREAK) {
|
2017-01-18 23:13:50 +03:00
|
|
|
if (doc.breakContents) {
|
2017-01-28 18:50:22 +03:00
|
|
|
cmds.push([ind, mode, doc.breakContents]);
|
2017-01-18 23:13:50 +03:00
|
|
|
}
|
|
|
|
}
|
2018-06-21 05:29:42 +03:00
|
|
|
if (groupMode === MODE_FLAT) {
|
2017-01-18 23:13:50 +03:00
|
|
|
if (doc.flatContents) {
|
2017-01-28 18:50:22 +03:00
|
|
|
cmds.push([ind, mode, doc.flatContents]);
|
2017-01-18 23:13:50 +03:00
|
|
|
}
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
2017-01-05 20:21:57 +03:00
|
|
|
|
2017-01-26 22:57:43 +03:00
|
|
|
break;
|
2018-06-21 05:29:42 +03:00
|
|
|
}
|
2017-01-26 22:57:43 +03:00
|
|
|
case "line-suffix":
|
2017-02-13 18:13:16 +03:00
|
|
|
lineSuffix.push([ind, mode, doc.contents]);
|
2016-12-23 21:38:10 +03:00
|
|
|
break;
|
2017-02-23 20:26:26 +03:00
|
|
|
case "line-suffix-boundary":
|
|
|
|
if (lineSuffix.length > 0) {
|
|
|
|
cmds.push([ind, mode, { type: "line", hard: true }]);
|
|
|
|
}
|
|
|
|
break;
|
2017-01-13 23:03:53 +03:00
|
|
|
case "line":
|
|
|
|
switch (mode) {
|
|
|
|
case MODE_FLAT:
|
|
|
|
if (!doc.hard) {
|
|
|
|
if (!doc.soft) {
|
|
|
|
out.push(" ");
|
|
|
|
|
|
|
|
pos += 1;
|
|
|
|
}
|
2017-01-05 20:21:57 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
// This line was forced into the output even if we
|
|
|
|
// were in flattened mode, so we need to tell the next
|
|
|
|
// group that no matter what, it needs to remeasure
|
|
|
|
// because the previous measurement didn't accurately
|
|
|
|
// capture the entire expression (this is necessary
|
|
|
|
// for nested groups)
|
|
|
|
shouldRemeasure = true;
|
|
|
|
}
|
2017-05-21 22:11:09 +03:00
|
|
|
// fallthrough
|
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
case MODE_BREAK:
|
2017-02-13 18:13:16 +03:00
|
|
|
if (lineSuffix.length) {
|
|
|
|
cmds.push([ind, mode, doc]);
|
|
|
|
[].push.apply(cmds, lineSuffix.reverse());
|
|
|
|
lineSuffix = [];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (doc.literal) {
|
2018-01-17 07:52:42 +03:00
|
|
|
if (ind.root) {
|
|
|
|
out.push(newLine, ind.root.value);
|
|
|
|
pos = ind.root.length;
|
|
|
|
} else {
|
|
|
|
out.push(newLine);
|
|
|
|
pos = 0;
|
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
} else {
|
2018-10-06 15:18:52 +03:00
|
|
|
pos -= trim(out);
|
2017-12-05 00:59:27 +03:00
|
|
|
out.push(newLine + ind.value);
|
|
|
|
pos = ind.length;
|
2017-01-13 23:03:53 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2016-12-23 21:38:10 +03:00
|
|
|
break;
|
2017-01-13 23:03:53 +03:00
|
|
|
default:
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
|
|
|
}
|
2017-01-05 20:21:57 +03:00
|
|
|
}
|
2017-06-02 01:52:29 +03:00
|
|
|
|
|
|
|
const cursorPlaceholderIndex = out.indexOf(cursor.placeholder);
|
|
|
|
if (cursorPlaceholderIndex !== -1) {
|
2018-05-01 15:42:59 +03:00
|
|
|
const otherCursorPlaceholderIndex = out.indexOf(
|
|
|
|
cursor.placeholder,
|
|
|
|
cursorPlaceholderIndex + 1
|
|
|
|
);
|
2017-06-02 01:52:29 +03:00
|
|
|
const beforeCursor = out.slice(0, cursorPlaceholderIndex).join("");
|
2018-05-01 15:42:59 +03:00
|
|
|
const aroundCursor = out
|
|
|
|
.slice(cursorPlaceholderIndex + 1, otherCursorPlaceholderIndex)
|
|
|
|
.join("");
|
|
|
|
const afterCursor = out.slice(otherCursorPlaceholderIndex + 1).join("");
|
2017-06-02 01:52:29 +03:00
|
|
|
|
|
|
|
return {
|
2018-05-01 15:42:59 +03:00
|
|
|
formatted: beforeCursor + aroundCursor + afterCursor,
|
|
|
|
cursorNodeStart: beforeCursor.length,
|
|
|
|
cursorNodeText: aroundCursor
|
2017-06-02 01:52:29 +03:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
return { formatted: out.join("") };
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
|
|
|
|
2017-01-20 21:12:37 +03:00
|
|
|
module.exports = { printDocToString };
|