Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 39 additions & 21 deletions src/printer.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
hasEmptyBody,
isNextLineEmptyAfterNamespace,
shouldPrintHardlineBeforeTrailingComma,
isHeredocNode,
isDocNode,
getAncestorNode,
isReferenceLikeNode,
Expand Down Expand Up @@ -626,7 +627,7 @@ function printArgumentsList(path, options, print, argumentsKey = "arguments") {
node.kind
))
? indent([
lastArg && shouldPrintHardlineBeforeTrailingComma(lastArg)
lastArg && shouldPrintHardlineBeforeTrailingComma(lastArg, options)
? hardline
: "",
",",
Expand Down Expand Up @@ -1648,6 +1649,32 @@ function isStringOnItsOwnLine(node, text) {
);
}

// `foo(<<<'SQL' ... SQL)` and `foo("multi-line string")`: a single string-like argument
// stays on the call's line instead of breaking the argument list (PER-CS 4.7:
// one argument spanning several lines does not split the list). A heredoc only
// hugs from PHP 7.3 on, when its closing marker may be followed by `)`.
function shouldHugSingleStringArgument(node, options) {
if (node.arguments.length !== 1) {
return false;
}

const [argument] = node.arguments;

return (
isStringOnItsOwnLine(argument, options.originalText) ||
(options.phpVersion >= 7.3 && isHeredocNode(argument))
);
}

function printSingleStringArgument(path, print) {
const [argument] = path.node.arguments;
const printed = path.map(print, "arguments");

// PER-CS 10: the heredoc body and closing marker are indented once past the
// statement; a quoted string keeps its literal lines as they are.
return ["(", isHeredocNode(argument) ? indent(printed) : printed, ")"];
}

function printComposedTypes(path, print, glue) {
return group(
path.map(({ isFirst }) => (isFirst ? [print()] : [glue, print()]), "types")
Expand Down Expand Up @@ -2162,17 +2189,9 @@ function printNode(path, options, print) {

return node.kind;
case "call": {
// Multiline strings as single arguments
if (
node.arguments.length === 1 &&
isStringOnItsOwnLine(node.arguments[0], options.originalText)
) {
return [
print("what"),
"(",
join(", ", path.map(print, "arguments")),
")",
];
// Multiline strings and heredocs as single arguments
if (shouldHugSingleStringArgument(node, options)) {
return [print("what"), printSingleStringArgument(path, print)];
}

// chain: Call (*LookupNode (Call (*LookupNode (...))))
Expand All @@ -2186,19 +2205,16 @@ function printNode(path, options, print) {
const isAnonymousClassNode =
node.what && node.what.kind === "class" && node.what.isAnonymous;

// Multiline strings as single arguments
// Multiline strings and heredocs as single arguments
if (
!isAnonymousClassNode &&
node.arguments.length === 1 &&
isStringOnItsOwnLine(node.arguments[0], options.originalText)
shouldHugSingleStringArgument(node, options)
) {
return [
"new ",
...path.call(printAttrs, "what"),
print("what"),
"(",
join(", ", path.map(print, "arguments")),
")",
printSingleStringArgument(path, print),
];
}

Expand Down Expand Up @@ -2590,7 +2606,8 @@ function printNode(path, options, print) {
ifBreak(
!needsForcedTrailingComma && shouldPrintComma(options, 5.0)
? [
lastElem && shouldPrintHardlineBeforeTrailingComma(lastElem)
lastElem &&
shouldPrintHardlineBeforeTrailingComma(lastElem, options)
? hardline
: "",
",",
Expand Down Expand Up @@ -2942,7 +2959,8 @@ function printNode(path, options, print) {
node.type === "heredoc" ? linebreak : "",
...path.map(print, "value"),
getEncapsedQuotes(node, { opening: false }),
node.type === "heredoc" && docShouldHaveTrailingNewline(path)
node.type === "heredoc" &&
docShouldHaveTrailingNewline(path, options)
? hardline
: "",
];
Expand All @@ -2969,7 +2987,7 @@ function printNode(path, options, print) {
join(linebreak, node.value.split("\n")),
linebreak,
node.label,
docShouldHaveTrailingNewline(path) ? hardline : "",
docShouldHaveTrailingNewline(path, options) ? hardline : "",
];
}
case "name":
Expand Down
46 changes: 33 additions & 13 deletions src/util.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,14 @@ function isDocNode(node) {
* Heredoc/Nowdoc nodes need a trailing linebreak if they
* appear as function arguments or array elements
*/
function docShouldHaveTrailingNewline(path, recurse = 0) {
// Before PHP 7.3 the closing marker of a heredoc or nowdoc had to be alone on
// its line, so anything following it (`,`, `:`, `=>`) needed a line break.
// Flexible heredocs (7.3+) lift that restriction; the marker is then followed
// by the comma, as in PER-CS 10. Two exceptions keep the line break in every
// version: a concatenation operator, which the `bin` printer lays out on its
// own line, and the `:` of a `case`, which php-parser does not accept right
// after a closing marker although PHP does.
function docShouldHaveTrailingNewline(path, options, recurse = 0) {
const node = path.getNode(recurse);
const parent = path.getNode(recurse + 1);
const parentParent = path.getNode(recurse + 2);
Expand All @@ -237,6 +244,10 @@ function docShouldHaveTrailingNewline(path, recurse = 0) {
return false;
}

if (options.phpVersion >= 7.3 && !["bin", "case"].includes(parent.kind)) {
return false;
}

if (
(parentParent &&
["call", "new", "echo"].includes(parentParent.kind) &&
Expand Down Expand Up @@ -271,7 +282,8 @@ function docShouldHaveTrailingNewline(path, recurse = 0) {

if (parent.kind === "bin") {
return (
parent.left === node || docShouldHaveTrailingNewline(path, recurse + 1)
parent.left === node ||
docShouldHaveTrailingNewline(path, options, recurse + 1)
);
}

Expand Down Expand Up @@ -319,7 +331,7 @@ function docShouldHaveTrailingNewline(path, recurse = 0) {
}

if (parent.kind === "retif") {
return docShouldHaveTrailingNewline(path, recurse + 1);
return docShouldHaveTrailingNewline(path, options, recurse + 1);
}

return false;
Expand Down Expand Up @@ -564,19 +576,26 @@ function isNextLineEmptyAfterNamespace(text, node) {
return hasNewline(text, idx);
}

function shouldPrintHardlineBeforeTrailingComma(lastElem) {
if (
lastElem.kind === "nowdoc" ||
(lastElem.kind === "encapsed" && lastElem.type === "heredoc")
) {
function isHeredocNode(node) {
return (
node.kind === "nowdoc" ||
(node.kind === "encapsed" && node.type === "heredoc")
);
}

// Before PHP 7.3 the closing marker of a heredoc or nowdoc had to be alone on
// its line, so a trailing comma after it needed a line of its own. Flexible
// heredocs (7.3+) allow `LABEL,`, which is also what PER-CS 10 shows.
function shouldPrintHardlineBeforeTrailingComma(lastElem, options) {
if (options.phpVersion >= 7.3) {
return false;
}

if (isHeredocNode(lastElem)) {
return true;
}

if (
lastElem.kind === "entry" &&
(lastElem.value.kind === "nowdoc" ||
(lastElem.value.kind === "encapsed" && lastElem.value.type === "heredoc"))
) {
if (lastElem.kind === "entry" && isHeredocNode(lastElem.value)) {
return true;
}

Expand Down Expand Up @@ -754,6 +773,7 @@ export {
hasEmptyBody,
isNextLineEmptyAfterNamespace,
shouldPrintHardlineBeforeTrailingComma,
isHeredocNode,
isDocNode,
getAncestorNode,
normalizeMagicMethodName,
Expand Down
26 changes: 8 additions & 18 deletions tests/call/__snapshots__/jsfmt.spec.mjs.snap
Original file line number Diff line number Diff line change
Expand Up @@ -655,55 +655,45 @@ string
$var\`,
);

call(
<<<FOO
call(<<<FOO
string
string
string
$var
FOO
,
);
FOO);

call(
<<<'FOO'
call(<<<'FOO'
string
string
string
FOO
,
);
FOO);

call(
<<<FOO
string
string
string
$var
FOO
,
FOO,
<<<FOO
string
string
string
$var
FOO
,
FOO,
);

call(
<<<'FOO'
string
string
string
FOO
,
FOO,
<<<'FOO'
string
string
string
FOO
,
FOO,
);

$var = ($this->foo)();
Expand Down
3 changes: 1 addition & 2 deletions tests/comments/__snapshots__/jsfmt.spec.mjs.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2760,8 +2760,7 @@ echo // Comment
Example of string
spanning multiple lines
using heredoc syntax.
EOD
,
EOD,
$a;

================================================================================
Expand Down
6 changes: 2 additions & 4 deletions tests/echo/__snapshots__/jsfmt.spec.mjs.snap
Original file line number Diff line number Diff line change
Expand Up @@ -427,16 +427,14 @@ echo <<<EOD
Example of string
spanning multiple lines
using heredoc syntax.
EOD
,
EOD,
"test";

echo <<<'EOD'
Example of string
spanning multiple lines
using nowdoc syntax.
EOD
,
EOD,
"test";

echo 'string
Expand Down
Loading
Loading