Repository: prettier/plugin-php
Pull request #503 (2018-08-01 09:55:20)

Add actual inline nodes instead of PSEUDO-nodes

czosel: Hi @evilebottnawi,

I took another look at the issue around `PSEUDO_INLINE_PLACEHOLDER` and it seems to me that it shouldn't be fixed inside the parser, because the official PHP parser behaves exactly the same way, i.e. it doesn't create inline nodes for this case:
```php
<?php echo 1; ?>
<?php echo 2; ?>
```

Instead, I tried adding the inline nodes to the AST on our side before printing anything. Overall, it seems to work! I'll add some comments to explain what isn't working yet.

Patch:
diff --git a/src/parser.js b/src/parser.js
index 7593623..dff4016 100644
--- a/src/parser.js
+++ b/src/parser.js
@@ -1,16 +1,18 @@
 "use strict";
 const engine = require("php-parser");
 
-function parse(text) {
-  // By default php parser don't create `inline` node between `?>\n<?`, we recreate their
-  // Need add option to parser to avoid this behaviour
-  text = text.replace(/\?>\n<\?/g, "?>\n___PSEUDO_INLINE_PLACEHOLDER___<?");
+function inBetweenLines(line, node, nextNode) {
+  return (
+    nextNode && node.loc.end.line <= line && nextNode.loc.start.line > line
+  );
+}
 
+function parse(text) {
   // initialize a new parser instance
   const parser = new engine({
-    // some options :
     parser: {
-      extractDoc: true
+      extractDoc: true,
+      extractTokens: true
     },
     ast: {
       withPositions: true,
@@ -19,6 +21,32 @@ function parse(text) {
   });
 
   const ast = parser.parseCode(text);
+
+  // parser doesn't create `inline` node between `?>\n<?`, so we add them manually
+  const missingLinebreaks = ast.tokens.filter((token, index, tokens) => {
+    return (
+      token[0] === "T_CLOSE_TAG" &&
+      token[1] === "?>\n" &&
+      tokens[index + 1] &&
+      tokens[index + 1][0] === "T_OPEN_TAG"
+    );
+  });
+  const lines = missingLinebreaks.map(token => token[2]);
+
+  const astIndices = ast.children.reduce((acc, child, index, children) => {
+    if (lines.find(line => inBetweenLines(line, child, children[index + 1]))) {
+      acc.push(index + 1);
+    }
+    return acc;
+  }, []);
+  astIndices.forEach((i, j) => {
+    ast.children.splice(i + j, 0, {
+      kind: "inline",
+      raw: "\n",
+      value: ""
+    });
+  });
+
   // https://github.com/glayzzle/php-parser/issues/155
   // currently inline comments include the line break at the end, we need to
   // strip those out and update the end location for each comment manually
diff --git a/src/printer.js b/src/printer.js
index bc2b595..eafe5e2 100644
--- a/src/printer.js
+++ b/src/printer.js
@@ -1053,10 +1053,7 @@ function printExpression(path, options, print) {
             : ""
         ]);
       case "inline":
-        return join(
-          literalline,
-          node.raw.replace("___PSEUDO_INLINE_PLACEHOLDER___", "").split("\n")
-        );
+        return join(literalline, node.raw.split("\n"));
       case "magic":
         // for magic constant we prefer upper case
         return node.value.toUpperCase();


Test patch:
diff --git a/tests/inline/__snapshots__/jsfmt.spec.js.snap b/tests/inline/__snapshots__/jsfmt.spec.js.snap
index 406e5bc..7f17943 100644
--- a/tests/inline/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/inline/__snapshots__/jsfmt.spec.js.snap
@@ -1321,8 +1321,10 @@ exports[`mixed-32.php 1`] = `
     <?php echo 'test'; ?>
 <div></div>
 <div></div>
-<?php if (true); ?>
-<?php echo 'test'; ?>
+<?php
+if (true);
+echo 'test';
+?>
 <div></div>
 
 `;