Add PrintResult for doc printer (#18372)
This commit is contained in:
1 parent
c4fb922a0f
commit
58ff4642dd
4 files changed
+110
-70
No files matched your search
@@ -1,5 +1,6 @@
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs/promises";
|
||||
import prettyBytes from "pretty-bytes";
|
||||
import * as prettierProduction from "../node_modules/prettier/index.mjs";
|
||||
import * as prettierDevelopment from "../src/index.js";
|
||||
import { runBenchmark } from "./utilities.js";
|
||||
@@ -7,27 +8,36 @@ import { runBenchmark } from "./utilities.js";
|
||||
assert.notEqual(prettierProduction.version, prettierDevelopment.version);
|
||||
|
||||
const text = await fs.readFile(new URL(import.meta.url), "utf8");
|
||||
// @ts-expect-error -- No types
|
||||
const doc = await prettierDevelopment.__debug.printToDoc(text, {
|
||||
parser: "babel",
|
||||
cursorOffset: text.indexOf("cursorOffset"),
|
||||
});
|
||||
|
||||
const run = (prettier) =>
|
||||
prettier.doc.printer.printDocToString(doc, { printWidth: 80, tabWidth: 2 });
|
||||
for (const size of [1, 1e1, 1e2, 1e3]) {
|
||||
const doc = await Promise.all(
|
||||
Array.from({ length: size }, (_, index) =>
|
||||
// @ts-expect-error -- No types
|
||||
prettierDevelopment.__debug.printToDoc(text, {
|
||||
parser: "babel",
|
||||
cursorOffset: index === 0 ? text.indexOf("cursorOffset") : undefined,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
const expected = await run(prettierProduction);
|
||||
const run = (prettier) =>
|
||||
prettier.doc.printer.printDocToString(doc, { printWidth: 80, tabWidth: 2 });
|
||||
|
||||
await runBenchmark(
|
||||
{
|
||||
name: "printDocToString",
|
||||
assert: (result) => assert.deepEqual(result, expected),
|
||||
},
|
||||
[
|
||||
{ name: "Development", prettier: prettierDevelopment },
|
||||
{ name: "Production", prettier: prettierProduction },
|
||||
].map(({ name, prettier }) => ({
|
||||
name,
|
||||
implementation: () => run(prettier),
|
||||
})),
|
||||
);
|
||||
const expected = await run(prettierProduction);
|
||||
|
||||
await runBenchmark(
|
||||
{
|
||||
name: `printDocToString (${size}, ${prettyBytes(text.length * size)})`,
|
||||
assert: (result) => assert.deepEqual(result, expected),
|
||||
warmupIterations: 1,
|
||||
iterations: size > 5e3 ? 1 : size > 1e2 ? 2 : undefined,
|
||||
},
|
||||
[
|
||||
{ name: "Development", prettier: prettierDevelopment },
|
||||
{ name: "Production", prettier: prettierProduction },
|
||||
].map(({ name, prettier }) => ({
|
||||
name,
|
||||
implementation: () => run(prettier),
|
||||
})),
|
||||
);
|
||||
}
|
||||
@@ -28,7 +28,7 @@ async function runBenchmark(options, implementations) {
|
||||
|
||||
const { assert, ...benchOptions } = options;
|
||||
|
||||
const bench = new Bench(benchOptions);
|
||||
const bench = new Bench({ throws: true, ...benchOptions });
|
||||
|
||||
let error;
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import { trimIndentation } from "./trim-indentation.js";
|
||||
|
||||
class printResult {
|
||||
/** @type {string[]} */
|
||||
#settledTexts = [];
|
||||
#unsettledText = "";
|
||||
#settledTextLength = 0;
|
||||
/** @type {number[]} */
|
||||
#settledPositions = [];
|
||||
/** @type {number[]} */
|
||||
#unsettledPositions = [];
|
||||
|
||||
#settle() {
|
||||
const text = this.#unsettledText;
|
||||
|
||||
if (text !== "") {
|
||||
this.#settledTexts.push(text);
|
||||
this.#settledTextLength += text.length;
|
||||
this.#unsettledText = "";
|
||||
}
|
||||
|
||||
for (const position of this.#unsettledPositions) {
|
||||
this.#settledPositions.push(Math.min(position, this.#settledTextLength));
|
||||
}
|
||||
this.#unsettledPositions.length = 0;
|
||||
}
|
||||
|
||||
markPosition() {
|
||||
if (this.#settledPositions.length + this.#unsettledPositions.length >= 2) {
|
||||
throw new Error("There are too many 'cursor' in doc.");
|
||||
}
|
||||
|
||||
this.#unsettledPositions.push(
|
||||
this.#settledTextLength + this.#unsettledText.length,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@param {string} text
|
||||
*/
|
||||
write(text) {
|
||||
this.#unsettledText += text;
|
||||
}
|
||||
|
||||
trim() {
|
||||
const { text: trimmed, count } = trimIndentation(this.#unsettledText);
|
||||
|
||||
this.#unsettledText = trimmed;
|
||||
this.#settle();
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
finish() {
|
||||
this.#settle();
|
||||
|
||||
return {
|
||||
text: this.#settledTexts.join(""),
|
||||
positions: this.#settledPositions,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default printResult;
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
import { getDocType, propagateBreaks } from "../utilities/index.js";
|
||||
import InvalidDocError from "../utilities/invalid-doc-error.js";
|
||||
import { makeAlign, makeIndent, ROOT_INDENT } from "./indent.js";
|
||||
import PrintResult from "./print-result.js";
|
||||
import { trimIndentation } from "./trim-indentation.js";
|
||||
|
||||
/**
|
||||
@@ -187,16 +188,11 @@ function printDocToString(doc, options) {
|
||||
// commands to the array instead of recursively calling `print`.
|
||||
/** @type Command[] */
|
||||
const commands = [{ indent: ROOT_INDENT, mode: MODE_BREAK, doc }];
|
||||
let output = "";
|
||||
let shouldRemeasure = false;
|
||||
/** @type Command[] */
|
||||
const lineSuffix = [];
|
||||
const cursorPositions = [];
|
||||
|
||||
/** @type string[] */
|
||||
const settledOutput = [];
|
||||
const settledCursorPositions = [];
|
||||
let settledTextLength = 0;
|
||||
const result = new PrintResult();
|
||||
|
||||
propagateBreaks(doc);
|
||||
|
||||
@@ -208,7 +204,7 @@ function printDocToString(doc, options) {
|
||||
newLine !== "\n" ? doc.replaceAll("\n", newLine) : doc;
|
||||
// Plugins may print single string, should skip measure the width
|
||||
if (formatted) {
|
||||
output += formatted;
|
||||
result.write(formatted);
|
||||
if (commands.length > 0) {
|
||||
position += getStringWidth(formatted);
|
||||
}
|
||||
@@ -223,10 +219,7 @@ function printDocToString(doc, options) {
|
||||
break;
|
||||
|
||||
case DOC_TYPE_CURSOR:
|
||||
if (cursorPositions.length >= 2) {
|
||||
throw new Error("There are too many 'cursor' in doc.");
|
||||
}
|
||||
cursorPositions.push(settledTextLength + output.length);
|
||||
result.markPosition();
|
||||
break;
|
||||
|
||||
case DOC_TYPE_INDENT:
|
||||
@@ -246,7 +239,7 @@ function printDocToString(doc, options) {
|
||||
break;
|
||||
|
||||
case DOC_TYPE_TRIM:
|
||||
trim();
|
||||
position -= result.trim();
|
||||
break;
|
||||
|
||||
case DOC_TYPE_GROUP:
|
||||
@@ -481,8 +474,7 @@ function printDocToString(doc, options) {
|
||||
case MODE_FLAT:
|
||||
if (!doc.hard) {
|
||||
if (!doc.soft) {
|
||||
output += " ";
|
||||
|
||||
result.write(" ");
|
||||
position += 1;
|
||||
}
|
||||
|
||||
@@ -506,17 +498,17 @@ function printDocToString(doc, options) {
|
||||
}
|
||||
|
||||
if (doc.literal) {
|
||||
output += newLine;
|
||||
result.write(newLine);
|
||||
position = 0;
|
||||
if (indent.root) {
|
||||
if (indent.root.value) {
|
||||
output += indent.root.value;
|
||||
result.write(indent.root.value);
|
||||
}
|
||||
position = indent.root.length;
|
||||
}
|
||||
} else {
|
||||
trim();
|
||||
output += newLine + indent.value;
|
||||
result.trim();
|
||||
result.write(newLine + indent.value);
|
||||
position = indent.length;
|
||||
}
|
||||
break;
|
||||
@@ -543,10 +535,9 @@ function printDocToString(doc, options) {
|
||||
}
|
||||
}
|
||||
|
||||
const formatted = settledOutput.join("") + output;
|
||||
const finalCursorPositions = [...settledCursorPositions, ...cursorPositions];
|
||||
const { text: formatted, positions: cursorPositions } = result.finish();
|
||||
|
||||
if (finalCursorPositions.length !== 2) {
|
||||
if (cursorPositions.length !== 2) {
|
||||
// If the doc contained ONE cursor command,
|
||||
// instead of the expected zero or two. If the doc being printed was
|
||||
// returned by printAstToDoc, then the only ways this can have happened
|
||||
@@ -575,37 +566,12 @@ function printDocToString(doc, options) {
|
||||
return { formatted };
|
||||
}
|
||||
|
||||
const cursorNodeStart = finalCursorPositions[0];
|
||||
const [cursorNodeStart, cursorNodeEnd] = cursorPositions;
|
||||
return {
|
||||
formatted,
|
||||
cursorNodeStart,
|
||||
cursorNodeText: formatted.slice(
|
||||
cursorNodeStart,
|
||||
finalCursorPositions.at(-1),
|
||||
),
|
||||
cursorNodeText: formatted.slice(cursorNodeStart, cursorNodeEnd),
|
||||
};
|
||||
|
||||
function trim() {
|
||||
const { text: trimmed, count } = trimIndentation(output);
|
||||
|
||||
if (trimmed) {
|
||||
settledOutput.push(trimmed);
|
||||
settledTextLength += trimmed.length;
|
||||
}
|
||||
|
||||
output = "";
|
||||
position -= count;
|
||||
|
||||
if (cursorPositions.length > 0) {
|
||||
settledCursorPositions.push(
|
||||
...cursorPositions.map((position) =>
|
||||
Math.min(position, settledTextLength),
|
||||
),
|
||||
);
|
||||
|
||||
cursorPositions.length = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { printDocToString };
|
||||
Reference in new issue
Block a user