diff --git a/src/doc-builders.js b/src/doc-builders.js index 82c7221bbe06..a9840b5392e8 100644 --- a/src/doc-builders.js +++ b/src/doc-builders.js @@ -51,6 +51,12 @@ function conditionalGroup(states, opts) { ); } +function fill(parts) { + parts.forEach(assertDoc); + + return { type: "fill", parts }; +} + function ifBreak(breakContents, flatContents) { if (breakContents) { assertDoc(breakContents); @@ -100,6 +106,7 @@ module.exports = { literalline, group, conditionalGroup, + fill, lineSuffix, lineSuffixBoundary, breakParent, diff --git a/src/doc-debug.js b/src/doc-debug.js index 79b4a3c5ac94..6576786835f7 100644 --- a/src/doc-debug.js +++ b/src/doc-debug.js @@ -91,10 +91,21 @@ function printDoc(doc) { ")"; } + if (doc.type === "fill") { + return ("fill") + + "(" + + doc.parts.map(printDoc).join(", ") + + ")"; + } + if (doc.type === "line-suffix") { return "lineSuffix(" + printDoc(doc.contents) + ")"; } + if (doc.type === "line-suffix-boundary") { + return "lineSuffixBoundary()"; + } + throw new Error("Unknown doc type " + doc.type); } diff --git a/src/doc-printer.js b/src/doc-printer.js index 82b493026d3e..b80ec70d99b8 100644 --- a/src/doc-printer.js +++ b/src/doc-printer.js @@ -1,9 +1,16 @@ "use strict"; +var docBuilders = require("./doc-builders"); +var concat = docBuilders.concat; +var line = docBuilders.line; +var softline = docBuilders.softline; +var fill = docBuilders.fill; +var ifBreak = docBuilders.ifBreak; + const MODE_BREAK = 1; const MODE_FLAT = 2; -function fits(next, restCommands, width) { +function fits(next, restCommands, width, mustBeFlat = false) { let restIdx = restCommands.length; const cmds = [next]; while (width >= 0) { @@ -39,8 +46,19 @@ function fits(next, restCommands, width) { break; case "group": + if (mustBeFlat && doc.break) { + return false; + } cmds.push([ind, doc.break ? MODE_BREAK : mode, doc.contents]); + break; + case "fill": + // include number of spaces + cmds.push([ind, mode, " ".repeat(doc.parts.length - 1)]); + for (var i = doc.parts.length - 1; i >= 0; i--) { + cmds.push([ind, mode, doc.parts[i]]); + } + break; case "if-break": if (mode === MODE_BREAK) { @@ -176,6 +194,51 @@ function printDocToString(doc, width, newLine) { break; } break; + case "fill": + // shouldRemeasure = false; + let rem = width - pos; + + const parts = doc.parts; + if (parts.length === 1) { + cmds.push([ind, MODE_BREAK, parts[0]]); + break; + } + + const first = parts[0]; + const second = parts[1]; + const remaining = parts.slice(1); + const firstCmd = [ind, MODE_FLAT, first]; + const remainingCmd = [ind, MODE_BREAK, fill(remaining)]; + + const firstAndSecondCmd = [ind, MODE_FLAT, concat([first, second])]; + + if (fits(firstAndSecondCmd, cmds, rem, true)) { + // console.log('first and second fit', [first, second]); + cmds.push(remainingCmd) + cmds.push([ind, MODE_FLAT, line]); + cmds.push(firstCmd); + } else if (fits(firstCmd, cmds, width - rem, true)) { + // console.log('only first fits', [first, second]); + cmds.push(remainingCmd) + if (typeof second !== 'string' || typeof first !== 'string') { + cmds.push([ind, MODE_BREAK, "{' '}"]); + } + cmds.push([ind, MODE_BREAK, line]); + cmds.push(firstCmd); + } else { + // console.log('neither fit', [first, second]); + out.push(newLine + " ".repeat(ind)); + pos = ind; + + cmds.push(remainingCmd); + if (typeof first !== 'string') { + cmds.push([ind, MODE_BREAK, line]); + cmds.push([ind, MODE_BREAK, "{' '}"]); + } + cmds.push([ind, MODE_BREAK, line]); + cmds.push([ind, MODE_BREAK, first]); + } + break; case "if-break": if (mode === MODE_BREAK) { if (doc.breakContents) { diff --git a/src/printer.js b/src/printer.js index 42354ddf2b5a..092d0b7c4bd2 100644 --- a/src/printer.js +++ b/src/printer.js @@ -16,6 +16,7 @@ var literalline = docBuilders.literalline; var group = docBuilders.group; var indent = docBuilders.indent; var conditionalGroup = docBuilders.conditionalGroup; +var fill = docBuilders.fill; var ifBreak = docBuilders.ifBreak; var breakParent = docBuilders.breakParent; var lineSuffixBoundary = docBuilders.lineSuffixBoundary; @@ -655,7 +656,7 @@ function genericPrintNoParens(path, options, print) { var isTypeAnnotation = n.type === "ObjectTypeAnnotation"; var isTypeScriptTypeAnnotaion = n.type === "TSTypeLiteral"; // Leave this here because we *might* want to make this - // configurable later -- flow accepts ";" for type separators, + // configurable later -- flow accepts ";" for type separators, // typescript accepts ";" and newlines var separator = isTypeAnnotation ? "," : ","; var fields = []; @@ -1069,7 +1070,7 @@ function genericPrintNoParens(path, options, print) { case "ForOfStatement": case "ForAwaitStatement": - // Babylon 7 removed ForAwaitStatement in favor of ForOfStatement + // Babylon 7 removed ForAwaitStatement in favor of ForOfStatement // with `"await": true`: // https://github.com/estree/estree/pull/138 const isAwait = (n.type === "ForAwaitStatement" || n.await); @@ -1853,8 +1854,8 @@ function genericPrintNoParens(path, options, print) { return "void"; case "TSAsExpression": return concat([ - path.call(print, "expression"), - " as ", + path.call(print, "expression"), + " as ", path.call(print, "typeAnnotation"), ]) case "TSArrayType": @@ -1868,17 +1869,17 @@ function genericPrintNoParens(path, options, print) { return concat([path.call(print, "typeName")]); case "TSCallSignature": return concat([ - "(", - join(", ", path.map(print, "parameters")), + "(", + join(", ", path.map(print, "parameters")), "): ", - path.call(print, "typeAnnotation"), + path.call(print, "typeAnnotation"), ]); case "TSConstructSignature": return concat([ - "new (", - join(", ", path.map(print, "parameters")), + "new (", + join(", ", path.map(print, "parameters")), "): ", - path.call(print, "typeAnnotation"), + path.call(print, "typeAnnotation"), ]); case "TSTypeQuery": return concat(["typeof ", path.call(print, "exprName")]); @@ -1886,12 +1887,12 @@ function genericPrintNoParens(path, options, print) { return concat(["(", path.call(print, "typeAnnotation"), ")"]); case "TSIndexSignature": return concat([ - "[", + "[", // This should only contain a single element, however TypeScript parses // it using parseDelimitedList that uses commas as delimiter. - join(", ", path.map(print, "parameters")), + join(", ", path.map(print, "parameters")), "]: ", - path.call(print, "typeAnnotation"), + path.call(print, "typeAnnotation"), ]); // TODO case "ClassHeritage": @@ -2762,62 +2763,83 @@ function printJSXChildren(path, options, print, jsxWhitespace) { : util.htmlEscapeInsideAngleBracket(child.value); const value = partiallyEscapedValue.replace(/\u00a0/g, " "); - if (/\S/.test(value)) { - // treat each line of text as its own entity - value.split(/(\n\s*)/).forEach(line => { - const newlines = line.match(/\n/g); - if (newlines) { - children.push(hardline); - - // allow one extra newline - if (newlines.length > 1) { - children.push(hardline); - } - return; - } - const beginSpace = /^\s+/.test(line); - if (beginSpace) { - children.push(jsxWhitespace); - children.push(softline); - } + // // console.log('value', value); + // if (/\S/.test(value)) { + // value.split(/(\s+)/).forEach(word => { + // // const newlines = word.match(/\n/g); + // // if (newlines) { + // // children.push(hardline); + // // + // // // allow one extra newline + // // if (newlines.length > 1) { + // // children.push(hardline); + // // } + // // return; + // // } + // + // const space = /^\s+$/.test(word); + // if (space) { + // words.push(line); + // return; + // } + // + // const stripped = word.replace(/^\s+|\s+$/g, ""); + // if (stripped) { + // words.push(stripped); + // } + // }); + // + // if (!isLineNext(util.getLast(words))) { + // words.push(softline); + // } + // } else if (/\n/.test(value)) { + // words.push(hardline); + // + // // allow one extra newline + // if (value.match(/\n/g).length > 1) { + // words.push(hardline); + // } + // } else if (/\s/.test(value)) { + // // whitespace-only without newlines, + // // eg; a single space separating two elements + // words.push(jsxWhitespace); + // words.push(softline); + // } + + value.replace(/^\s+|\s+$/g, "").split(/(\s+)/).forEach(word => { + if (word.length === 0) { + return; + } - const stripped = line.replace(/^\s+|\s+$/g, ""); - if (stripped) { - children.push(stripped); - } + const newlines = word.match(/\n/g); + if (newlines) { + children.push(hardline); - const endSpace = /\s+$/.test(line); - if (endSpace) { - children.push(softline); - children.push(jsxWhitespace); + // allow one extra newline + if (newlines.length > 1) { + children.push(hardline); } - }); - - if (!isLineNext(util.getLast(children))) { - children.push(softline); + return; } - } else if (/\n/.test(value)) { - children.push(hardline); - // allow one extra newline - if (value.match(/\n/g).length > 1) { - children.push(hardline); + const space = /^\s+$/.test(word); + if (space) { + // children.push(line); + return; } - } else if (/\s/.test(value)) { - // whitespace-only without newlines, - // eg; a single space separating two elements - children.push(jsxWhitespace); - children.push(softline); - } + + children.push(word); + }); + } else { children.push(print(childPath)); - - // add a line unless it's followed by a JSX newline - let next = n.children[i + 1]; - if (!(next && /^\s*\n/.test(next.value))) { - children.push(softline); - } + // + // // add a line unless it's followed by a JSX newline + // let next = n.children[i + 1]; + // if (!(next && /^\s*\n/.test(next.value))) { + // children.push(softline); + // } } }, "children" @@ -2932,7 +2954,7 @@ function printJSXElement(path, options, print) { groups.map( contents => Array.isArray(contents) - ? conditionalGroup([concat(contents)]) + ? fill(contents) : contents ) ) @@ -2957,7 +2979,7 @@ function printJSXElement(path, options, print) { } return conditionalGroup([ - group(concat([openingLines, concat(children), closingLines])), + group(concat([openingLines, fill(children), closingLines])), multiLineElem ]); } diff --git a/tests/jsx-significant-space/__snapshots__/jsfmt.spec.js.snap b/tests/jsx-significant-space/__snapshots__/jsfmt.spec.js.snap index 36c0f42c95dd..92faed0c2456 100644 --- a/tests/jsx-significant-space/__snapshots__/jsfmt.spec.js.snap +++ b/tests/jsx-significant-space/__snapshots__/jsfmt.spec.js.snap @@ -1,199 +1,17 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`test.js 1`] = ` -"after = - - foo bar - - -before = - - bar foo - - -before_break1 = - - foo - - -before_break2 = - - foo - - -after_break = - - foo - - -within = - - foo bar - - -break_components = +"x =
- - -

foobar bar bar

yep

-
-

nope

-
- -var x =
- hello hi sdkflsdfjk -
; + A + great and powerful monkey lord + attacked me! -nest_plz = -
-
-
-
-
- -regression_not_transformed_1 = - ; - -regression_not_transformed_2 = - ; - -similar_1 = - ; - -similar_2 = - ; - -similar_3 = - ; - -not_broken_end = -
- long text long text long text long text long text long text long text long text url long text long text -
- -not_broken_begin = -
-
long text long text long text long text long text long text long text long texturl long text long text + Please state your first name and occupation for the board of directors and let us know if you would very much like them to contact you in future?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -after = ( - - foo bar - -); - -before = ( - - bar foo - -); - -before_break1 = ( - - - {\\" \\"} - foo - -); - -before_break2 = ( - - - {\\" \\"} - foo - -); - -after_break = ( - - foo - {\\" \\"} - - -); - -within = ( - - foo bar - -); - -break_components = ( -
- - -

foobar bar bar

yep

-
-

nope

-
-); - -var x = ( -
- hello hi sdkflsdfjk -
-); - -nest_plz = ( -
-
-
-
-
-); - -regression_not_transformed_1 = ( - - {\\" \\"} - -); - -regression_not_transformed_2 = ( - - {\\" \\"} - -); - -similar_1 = ( - - {\\" \\"} - - -); - -similar_2 = ( - - - {\\" \\"} - -); - -similar_3 = ( - - - -); - -not_broken_end = ( -
- long text long text long text long text long text long text long text long text - {\\" \\"} - url - {\\" \\"} - long text long text -
-); - -not_broken_begin = ( +x = (
-
- {\\" \\"} - long text long text long text long text long text long text long text long text - url - {\\" \\"} - long text long text
); " diff --git a/tests/jsx-significant-space/test.js b/tests/jsx-significant-space/test.js index 6f25e76bfdb3..f74f78d831a1 100644 --- a/tests/jsx-significant-space/test.js +++ b/tests/jsx-significant-space/test.js @@ -1,74 +1,8 @@ -after = - - foo bar - - -before = - - bar foo - - -before_break1 = - - foo - - -before_break2 = - - foo - - -after_break = - - foo - - -within = - - foo bar - - -break_components = -
- - -

foobar bar bar

yep

-
-

nope

-
- -var x =
- hello hi sdkflsdfjk -
; - -nest_plz = +x =
-
-
-
-
- -regression_not_transformed_1 = - ; - -regression_not_transformed_2 = - ; - -similar_1 = - ; - -similar_2 = - ; + A + great and powerful monkey lord + attacked me! -similar_3 = - ; - -not_broken_end = -
- long text long text long text long text long text long text long text long text url long text long text -
- -not_broken_begin = -
-
long text long text long text long text long text long text long text long texturl long text long text + Please state your first name part address and occupation for the board of directors and let us know if you would very much like them to contact you in future?
diff --git a/tests_config/run_spec.js b/tests_config/run_spec.js index 710829e532e6..9337980fbcc3 100644 --- a/tests_config/run_spec.js +++ b/tests_config/run_spec.js @@ -52,6 +52,9 @@ function run_spec(dirname, options, additionalParsers) { expect(output).toEqual(verifyOutput); }); }); + + const doc = prettier.__debug.printToDoc(source, options); + console.log(prettier.__debug.formatDoc(doc)); } if (RUN_AST_TESTS) {