Improve computational time complexity for printing fill() (#16595)
This commit is contained in:
1 parent
92253a3f40
commit
c921abee76
2 files changed
+40
-24
No files matched your search
+14
-23
@@ -32,7 +32,7 @@ const MODE_FLAT = Symbol("MODE_FLAT");
|
||||
|
||||
const CURSOR_PLACEHOLDER = Symbol("cursor");
|
||||
|
||||
const IS_MUTABLE_FILL = Symbol("IS_MUTABLE_FILL");
|
||||
const DOC_FILL_PRINTED_LENGTH = Symbol("DOC_FILL_PRINTED_LENGTH");
|
||||
|
||||
function rootIndent() {
|
||||
return { value: "", length: 0, queue: [] };
|
||||
@@ -463,12 +463,15 @@ function printDocToString(doc, options) {
|
||||
case DOC_TYPE_FILL: {
|
||||
const rem = width - pos;
|
||||
|
||||
const offset = doc[DOC_FILL_PRINTED_LENGTH] ?? 0;
|
||||
const { parts } = doc;
|
||||
if (parts.length === 0) {
|
||||
const length = parts.length - offset;
|
||||
if (length === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
const [content, whitespace] = parts;
|
||||
const content = parts[offset + 0];
|
||||
const whitespace = parts[offset + 1];
|
||||
/** @type {Command} */
|
||||
const contentFlatCmd = { ind, mode: MODE_FLAT, doc: content };
|
||||
/** @type {Command} */
|
||||
@@ -482,7 +485,7 @@ function printDocToString(doc, options) {
|
||||
true,
|
||||
);
|
||||
|
||||
if (parts.length === 1) {
|
||||
if (length === 1) {
|
||||
if (contentFits) {
|
||||
cmds.push(contentFlatCmd);
|
||||
} else {
|
||||
@@ -496,7 +499,7 @@ function printDocToString(doc, options) {
|
||||
/** @type {Command} */
|
||||
const whitespaceBreakCmd = { ind, mode: MODE_BREAK, doc: whitespace };
|
||||
|
||||
if (parts.length === 2) {
|
||||
if (length === 2) {
|
||||
if (contentFits) {
|
||||
cmds.push(whitespaceFlatCmd, contentFlatCmd);
|
||||
} else {
|
||||
@@ -505,26 +508,14 @@ function printDocToString(doc, options) {
|
||||
break;
|
||||
}
|
||||
|
||||
const secondContent = parts[2];
|
||||
|
||||
// At this point we've handled the first pair (context, separator)
|
||||
// and will create a new *mutable* fill doc for the rest of the content.
|
||||
// Copying all the elements to a new array would make this algorithm quadratic,
|
||||
// which is unusable for large arrays (e.g. large texts in JSX).
|
||||
// https://github.com/prettier/prettier/issues/3263#issuecomment-344275152
|
||||
let remainingDoc = doc;
|
||||
if (doc[IS_MUTABLE_FILL]) {
|
||||
parts.splice(0, 2);
|
||||
} else {
|
||||
remainingDoc = {
|
||||
...doc,
|
||||
parts: parts.slice(2),
|
||||
[IS_MUTABLE_FILL]: true,
|
||||
};
|
||||
}
|
||||
const secondContent = parts[offset + 2];
|
||||
|
||||
/** @type {Command} */
|
||||
const remainingCmd = { ind, mode, doc: remainingDoc };
|
||||
const remainingCmd = {
|
||||
ind,
|
||||
mode,
|
||||
doc: { ...doc, [DOC_FILL_PRINTED_LENGTH]: offset + 2 },
|
||||
};
|
||||
|
||||
/** @type {Command} */
|
||||
const firstAndSecondContentFlatCmd = {
|
||||
|
||||
@@ -40,6 +40,31 @@ test("`printDocToString` should not manipulate docs", () => {
|
||||
|
||||
const lines = formatted.split("\n");
|
||||
expect(lines.length).toBeGreaterThan(1000);
|
||||
expect(endTime - startTime).toBeLessThan(1000);
|
||||
expect(endTime - startTime).toBeLessThan(100);
|
||||
}
|
||||
});
|
||||
|
||||
describe("`printDocToString` has linear time complexity at most to print fill()", () => {
|
||||
const baseSize = 3_000;
|
||||
const relativeMargin = 0.3;
|
||||
const baseTime = time(makeFill(baseSize));
|
||||
test.each([10_000, 20_000, 40_000])("numWords=%d", (numWords) => {
|
||||
const doc = makeFill(numWords);
|
||||
const ellapsed = time(doc);
|
||||
const ratio = numWords / baseSize;
|
||||
expect(ellapsed).toBeLessThan(baseTime * ratio * (1 + relativeMargin));
|
||||
});
|
||||
|
||||
function makeFill(numWords) {
|
||||
const WORD = "word";
|
||||
const parts = Array.from({ length: numWords }, () => WORD);
|
||||
return fill(join(line, parts));
|
||||
}
|
||||
|
||||
function time(doc) {
|
||||
const printOptions = { printWidth: 40, tabWidth: 2 };
|
||||
const start = performance.now();
|
||||
printDocToString(doc, printOptions);
|
||||
return performance.now() - start;
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user