diff --git a/commands.md b/commands.md index c9527be5e..28d32b4b4 100644 --- a/commands.md +++ b/commands.md @@ -271,6 +271,18 @@ With `negate: true`, corresponds to `ifBreak(doc, indent(doc), { groupId })` It doesn't make sense to apply `indentIfBreak` to the current group because "indent if the current group is broken" is the normal behavior of `indent`. That's why `groupId` is required. +### `label` + +_Added in v2.3.0_ + +```ts +declare function label(label: string, doc: Doc): Doc; +``` + +Mark a doc with a string label. This doesn't affect how the doc is printed, but can be useful for heuristics based on doc introspection. + +E.g., to decide how to print an assignment expression, we might want to know whether its right-hand side has been printed as a method call chain, not as a plain function call. If the method chain printing code uses `label` to mark its result, checking that condition can be as easy as `rightHandSideDoc.label === 'method-chain'`. + ### `hardlineWithoutBreakParent` and `literallineWithoutBreakParent` _Added in v2.3.0_ diff --git a/src/document/doc-builders.js b/src/document/doc-builders.js index 9747dcd18..4b0315070 100644 --- a/src/document/doc-builders.js +++ b/src/document/doc-builders.js @@ -259,6 +259,10 @@ function addAlignmentToDoc(doc, size, tabWidth) { return aligned; } +function label(label, contents) { + return { type: "label", label, contents }; +} + module.exports = { concat, join, @@ -284,4 +288,5 @@ module.exports = { dedent, hardlineWithoutBreakParent, literallineWithoutBreakParent, + label, }; diff --git a/src/document/doc-debug.js b/src/document/doc-debug.js index 41bda1c36..2499d323f 100644 --- a/src/document/doc-debug.js +++ b/src/document/doc-debug.js @@ -173,6 +173,10 @@ function printDocToDebug(doc) { return "lineSuffixBoundary"; } + if (doc.type === "label") { + return `label(${JSON.stringify(doc.label)}, ${printDoc(doc.contents)})`; + } + throw new Error("Unknown doc type " + doc.type); } diff --git a/src/document/doc-printer.js b/src/document/doc-printer.js index 64509a440..29f7a3c84 100644 --- a/src/document/doc-printer.js +++ b/src/document/doc-printer.js @@ -258,6 +258,9 @@ function fits(next, restCommands, width, options, hasLineSuffix, mustBeFlat) { return false; } break; + case "label": + cmds.push([ind, mode, doc.contents]); + break; } } } @@ -553,6 +556,9 @@ function printDocToString(doc, options) { break; } break; + case "label": + cmds.push([ind, mode, doc.contents]); + break; default: } } diff --git a/src/document/doc-utils.js b/src/document/doc-utils.js index 29333a4b2..796944900 100644 --- a/src/document/doc-utils.js +++ b/src/document/doc-utils.js @@ -227,8 +227,10 @@ function stripDocTrailingHardlineFromDoc(doc) { switch (doc.type) { case "align": case "indent": + case "indent-if-break": case "group": - case "line-suffix": { + case "line-suffix": + case "label": { const contents = stripDocTrailingHardlineFromDoc(doc.contents); return { ...doc, contents }; } @@ -270,6 +272,7 @@ function cleanDocFn(doc) { break; case "align": case "indent": + case "indent-if-break": case "line-suffix": if (!doc.contents) { return ""; diff --git a/src/language-js/print/member-chain.js b/src/language-js/print/member-chain.js index 2564fa507..07a9f9b77 100644 --- a/src/language-js/print/member-chain.js +++ b/src/language-js/print/member-chain.js @@ -24,7 +24,15 @@ const { const { locEnd } = require("../loc"); const { - builders: { join, hardline, group, indent, conditionalGroup, breakParent }, + builders: { + join, + hardline, + group, + indent, + conditionalGroup, + breakParent, + label, + }, utils: { willBreak }, } = require("../../document"); const printCallArguments = require("./call-arguments"); @@ -374,6 +382,8 @@ function printMemberChain(path, options, print) { ); } + let result; + // We don't want to print in one line if at least one of these conditions occurs: // * the chain has comments, // * the chain is an expression statement and all the arguments are literal-like ("fluent configuration" pattern), @@ -389,16 +399,18 @@ function printMemberChain(path, options, print) { printedGroups.slice(0, -1).some(willBreak) || lastGroupWillBreakAndOtherCallsHaveFunctionArguments() ) { - return group(expanded); + result = group(expanded); + } else { + result = [ + // We only need to check `oneLine` because if `expanded` is chosen + // that means that the parent group has already been broken + // naturally + willBreak(oneLine) || shouldHaveEmptyLineBeforeIndent ? breakParent : "", + conditionalGroup([oneLine, expanded]), + ]; } - return [ - // We only need to check `oneLine` because if `expanded` is chosen - // that means that the parent group has already been broken - // naturally - willBreak(oneLine) || shouldHaveEmptyLineBeforeIndent ? breakParent : "", - conditionalGroup([oneLine, expanded]), - ]; + return label("member-chain", result); } module.exports = printMemberChain; diff --git a/tests_integration/__tests__/debug-api.js b/tests_integration/__tests__/debug-api.js index f7f3a1f59..776f8e070 100644 --- a/tests_integration/__tests__/debug-api.js +++ b/tests_integration/__tests__/debug-api.js @@ -66,6 +66,7 @@ describe("API", () => { indentIfBreak, group, line, + label, } = builders; expect(formatDoc([indent(hardline), indent(literalline)])).toBe( @@ -86,5 +87,9 @@ describe("API", () => { expect( formatDoc(indentIfBreak(group(["1", line, "2"]), { groupId: "Q" })) ).toBe('indentIfBreak(group(["1", line, "2"]), { groupId: "Q" })'); + + expect(formatDoc(label("foo", group(["1", line, "2"])))).toBe( + 'label("foo", group(["1", line, "2"]))' + ); }); });