From ab72a2c11c806f3a8a5ef42314e291843e1b3e68 Mon Sep 17 00:00:00 2001 From: Georgii Dolzhykov Date: Sat, 23 Jul 2022 15:15:50 +0300 Subject: [PATCH] Refactor: simplify `fits` (#13154) --- src/document/doc-printer.js | 195 ++++++++++++++++-------------------- 1 file changed, 86 insertions(+), 109 deletions(-) diff --git a/src/document/doc-printer.js b/src/document/doc-printer.js index c0c31363b..b0d07af64 100644 --- a/src/document/doc-printer.js +++ b/src/document/doc-printer.js @@ -6,11 +6,16 @@ const getStringWidth = require("../utils/get-string-width.js"); const { fill, cursor, indent } = require("./doc-builders.js"); const { isConcat, getDocParts } = require("./doc-utils.js"); -/** @type {Record} */ +/** @typedef {typeof MODE_BREAK | typeof MODE_FLAT} Mode */ +/** @typedef {{ ind: any, doc: any, mode: Mode }} Command */ + +/** @type {Record} */ let groupModeMap; -const MODE_BREAK = 1; -const MODE_FLAT = 2; +// prettier-ignore +const MODE_BREAK = /** @type {const} */ (1); +// prettier-ignore +const MODE_FLAT = /** @type {const} */ (2); function rootIndent() { return { value: "", length: 0, queue: [] }; @@ -146,8 +151,17 @@ function trim(out) { return trimCount; } -function fits(next, restCommands, width, options, hasLineSuffix, mustBeFlat) { +/** + * @param {Command} next + * @param {Command[]} restCommands + * @param {number} width + * @param {boolean} hasLineSuffix + * @param {boolean} [mustBeFlat] + * @returns {boolean} + */ +function fits(next, restCommands, width, hasLineSuffix, mustBeFlat) { let restIdx = restCommands.length; + /** @type {Array>} */ const cmds = [next]; // `out` is only used for width counting because `trim` requires to look // backwards for space characters. @@ -157,119 +171,78 @@ function fits(next, restCommands, width, options, hasLineSuffix, mustBeFlat) { if (restIdx === 0) { return true; } - cmds.push(restCommands[restIdx - 1]); - - restIdx--; - + cmds.push(restCommands[--restIdx]); continue; } - const [ind, mode, doc] = cmds.pop(); + const { mode, doc } = cmds.pop(); if (typeof doc === "string") { out.push(doc); - width -= getStringWidth(doc); - } else if (isConcat(doc)) { + } else if (isConcat(doc) || doc.type === "fill") { const parts = getDocParts(doc); for (let i = parts.length - 1; i >= 0; i--) { - cmds.push([ind, mode, parts[i]]); + cmds.push({ mode, doc: parts[i] }); } } else { switch (doc.type) { case "indent": - cmds.push([makeIndent(ind, options), mode, doc.contents]); - - break; case "align": - cmds.push([makeAlign(ind, doc.n, options), mode, doc.contents]); - + case "indent-if-break": + case "label": + cmds.push({ mode, doc: doc.contents }); break; + case "trim": width += trim(out); - break; + case "group": { if (mustBeFlat && doc.break) { return false; } const groupMode = doc.break ? MODE_BREAK : mode; - cmds.push([ - ind, - groupMode, - // The most expanded state takes up the least space on the current line. + // The most expanded state takes up the least space on the current line. + const contents = doc.expandedStates && groupMode === MODE_BREAK ? getLast(doc.expandedStates) - : doc.contents, - ]); + : doc.contents; + cmds.push({ mode: groupMode, doc: contents }); break; } - case "fill": - for (let i = doc.parts.length - 1; i >= 0; i--) { - cmds.push([ind, mode, doc.parts[i]]); - } - break; - case "if-break": - case "indent-if-break": { + case "if-break": { const groupMode = doc.groupId ? groupModeMap[doc.groupId] || MODE_FLAT : mode; - if (groupMode === MODE_BREAK) { - const breakContents = - doc.type === "if-break" - ? doc.breakContents - : doc.negate - ? doc.contents - : indent(doc.contents); - if (breakContents) { - cmds.push([ind, mode, breakContents]); - } + const contents = + groupMode === MODE_BREAK ? doc.breakContents : doc.flatContents; + if (contents) { + cmds.push({ mode, doc: contents }); } - if (groupMode === MODE_FLAT) { - const flatContents = - doc.type === "if-break" - ? doc.flatContents - : doc.negate - ? indent(doc.contents) - : doc.contents; - if (flatContents) { - cmds.push([ind, mode, flatContents]); - } - } - break; } + case "line": - switch (mode) { - // fallthrough - case MODE_FLAT: - if (!doc.hard) { - if (!doc.soft) { - out.push(" "); - - width -= 1; - } - - break; - } - return true; - - case MODE_BREAK: - return true; + if (mode === MODE_BREAK || doc.hard) { + return true; + } + if (!doc.soft) { + out.push(" "); + width--; } break; + case "line-suffix": hasLineSuffix = true; break; + case "line-suffix-boundary": if (hasLineSuffix) { return false; } break; - case "label": - cmds.push([ind, mode, doc.contents]); - break; } } } @@ -285,13 +258,15 @@ function printDocToString(doc, options) { // 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`. - const cmds = [[rootIndent(), MODE_BREAK, doc]]; + /** @type Command[] */ + const cmds = [{ ind: rootIndent(), mode: MODE_BREAK, doc }]; const out = []; let shouldRemeasure = false; - let lineSuffix = []; + /** @type Command[] */ + const lineSuffix = []; while (cmds.length > 0) { - const [ind, mode, doc] = cmds.pop(); + const { ind, mode, doc } = cmds.pop(); if (typeof doc === "string") { const formatted = newLine !== "\n" ? doc.replace(/\n/g, newLine) : doc; @@ -300,7 +275,7 @@ function printDocToString(doc, options) { } else if (isConcat(doc)) { const parts = getDocParts(doc); for (let i = parts.length - 1; i >= 0; i--) { - cmds.push([ind, mode, parts[i]]); + cmds.push({ ind, mode, doc: parts[i] }); } } else { switch (doc.type) { @@ -309,11 +284,15 @@ function printDocToString(doc, options) { break; case "indent": - cmds.push([makeIndent(ind, options), mode, doc.contents]); + cmds.push({ ind: makeIndent(ind, options), mode, doc: doc.contents }); break; case "align": - cmds.push([makeAlign(ind, doc.n, options), mode, doc.contents]); + cmds.push({ + ind: makeAlign(ind, doc.n, options), + mode, + doc: doc.contents, + }); break; case "trim": @@ -324,11 +303,11 @@ function printDocToString(doc, options) { switch (mode) { case MODE_FLAT: if (!shouldRemeasure) { - cmds.push([ + cmds.push({ ind, - doc.break ? MODE_BREAK : MODE_FLAT, - doc.contents, - ]); + mode: doc.break ? MODE_BREAK : MODE_FLAT, + doc: doc.contents, + }); break; } @@ -337,11 +316,11 @@ function printDocToString(doc, options) { case MODE_BREAK: { shouldRemeasure = false; - const next = [ind, MODE_FLAT, doc.contents]; + const next = { ind, mode: MODE_FLAT, doc: doc.contents }; const rem = width - pos; const hasLineSuffix = lineSuffix.length > 0; - if (!doc.break && fits(next, cmds, rem, options, hasLineSuffix)) { + if (!doc.break && fits(next, cmds, rem, hasLineSuffix)) { cmds.push(next); } else { // Expanded states are a rare case where a document @@ -355,20 +334,20 @@ function printDocToString(doc, options) { const mostExpanded = getLast(doc.expandedStates); if (doc.break) { - cmds.push([ind, MODE_BREAK, mostExpanded]); + cmds.push({ ind, mode: MODE_BREAK, doc: mostExpanded }); break; } else { for (let i = 1; i < doc.expandedStates.length + 1; i++) { if (i >= doc.expandedStates.length) { - cmds.push([ind, MODE_BREAK, mostExpanded]); + cmds.push({ ind, mode: MODE_BREAK, doc: mostExpanded }); break; } else { const state = doc.expandedStates[i]; - const cmd = [ind, MODE_FLAT, state]; + const cmd = { ind, mode: MODE_FLAT, doc: state }; - if (fits(cmd, cmds, rem, options, hasLineSuffix)) { + if (fits(cmd, cmds, rem, hasLineSuffix)) { cmds.push(cmd); break; @@ -377,7 +356,7 @@ function printDocToString(doc, options) { } } } else { - cmds.push([ind, MODE_BREAK, doc.contents]); + cmds.push({ ind, mode: MODE_BREAK, doc: doc.contents }); } } @@ -386,7 +365,7 @@ function printDocToString(doc, options) { } if (doc.id) { - groupModeMap[doc.id] = getLast(cmds)[1]; + groupModeMap[doc.id] = getLast(cmds).mode; } break; // Fills each line with as much code as possible before moving to a new @@ -418,13 +397,12 @@ function printDocToString(doc, options) { } const [content, whitespace] = parts; - const contentFlatCmd = [ind, MODE_FLAT, content]; - const contentBreakCmd = [ind, MODE_BREAK, content]; + const contentFlatCmd = { ind, mode: MODE_FLAT, doc: content }; + const contentBreakCmd = { ind, mode: MODE_BREAK, doc: content }; const contentFits = fits( contentFlatCmd, [], rem, - options, lineSuffix.length > 0, true ); @@ -438,8 +416,8 @@ function printDocToString(doc, options) { break; } - const whitespaceFlatCmd = [ind, MODE_FLAT, whitespace]; - const whitespaceBreakCmd = [ind, MODE_BREAK, whitespace]; + const whitespaceFlatCmd = { ind, mode: MODE_FLAT, doc: whitespace }; + const whitespaceBreakCmd = { ind, mode: MODE_BREAK, doc: whitespace }; if (parts.length === 2) { if (contentFits) { @@ -456,20 +434,19 @@ function printDocToString(doc, options) { // 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 remainingCmd = { ind, mode, doc: fill(parts) }; const secondContent = parts[0]; - const firstAndSecondContentFlatCmd = [ + const firstAndSecondContentFlatCmd = { ind, - MODE_FLAT, - [content, whitespace, secondContent], - ]; + mode: MODE_FLAT, + doc: [content, whitespace, secondContent], + }; const firstAndSecondContentFits = fits( firstAndSecondContentFlatCmd, [], rem, - options, lineSuffix.length > 0, true ); @@ -494,7 +471,7 @@ function printDocToString(doc, options) { ? doc.contents : indent(doc.contents); if (breakContents) { - cmds.push([ind, mode, breakContents]); + cmds.push({ ind, mode, doc: breakContents }); } } if (groupMode === MODE_FLAT) { @@ -505,18 +482,18 @@ function printDocToString(doc, options) { ? indent(doc.contents) : doc.contents; if (flatContents) { - cmds.push([ind, mode, flatContents]); + cmds.push({ ind, mode, doc: flatContents }); } } break; } case "line-suffix": - lineSuffix.push([ind, mode, doc.contents]); + lineSuffix.push({ ind, mode, doc: doc.contents }); break; case "line-suffix-boundary": if (lineSuffix.length > 0) { - cmds.push([ind, mode, { type: "line", hard: true }]); + cmds.push({ ind, mode, doc: { type: "line", hard: true } }); } break; case "line": @@ -543,8 +520,8 @@ function printDocToString(doc, options) { case MODE_BREAK: if (lineSuffix.length > 0) { - cmds.push([ind, mode, doc], ...lineSuffix.reverse()); - lineSuffix = []; + cmds.push({ ind, mode, doc }, ...lineSuffix.reverse()); + lineSuffix.length = 0; break; } @@ -565,7 +542,7 @@ function printDocToString(doc, options) { } break; case "label": - cmds.push([ind, mode, doc.contents]); + cmds.push({ ind, mode, doc: doc.contents }); break; default: } @@ -575,7 +552,7 @@ function printDocToString(doc, options) { // there is no new line after the line-suffix. if (cmds.length === 0 && lineSuffix.length > 0) { cmds.push(...lineSuffix.reverse()); - lineSuffix = []; + lineSuffix.length = 0; } }