@@ -120,7 +120,7 @@ abstract class PrettyPrinterAbstract
120120 */
121121 protected $ removalMap ;
122122 /**
123- * @var mixed[] Map from "{$node->getType()}->{$subNode}" to [$find, $extraLeft, $extraRight].
123+ * @var mixed[] Map from "{$node->getType()}->{$subNode}" to [$find, $beforeToken, $ extraLeft, $extraRight].
124124 * $find is an optional token after which the insertion occurs. $extraLeft/Right
125125 * are optionally added before/after the main insertions.
126126 */
@@ -130,6 +130,7 @@ abstract class PrettyPrinterAbstract
130130 * between elements of this list subnode.
131131 */
132132 protected $ listInsertionMap ;
133+ protected $ emptyListInsertionMap ;
133134 /** @var int[] Map from "{$node->getType()}->{$subNode}" to token before which the modifiers
134135 * should be reprinted. */
135136 protected $ modifierChangeMap ;
@@ -479,6 +480,7 @@ public function printFormatPreserving(array $stmts, array $origStmts, array $ori
479480 $ this ->initializeRemovalMap ();
480481 $ this ->initializeInsertionMap ();
481482 $ this ->initializeListInsertionMap ();
483+ $ this ->initializeEmptyListInsertionMap ();
482484 $ this ->initializeModifierChangeMap ();
483485
484486 $ this ->resetState ();
@@ -487,7 +489,7 @@ public function printFormatPreserving(array $stmts, array $origStmts, array $ori
487489 $ this ->preprocessNodes ($ stmts );
488490
489491 $ pos = 0 ;
490- $ result = $ this ->pArray ($ stmts , $ origStmts , $ pos , 0 , 'stmts ' , null , "\n" );
492+ $ result = $ this ->pArray ($ stmts , $ origStmts , $ pos , 0 , 'File ' , ' stmts ' , null );
491493 if (null !== $ result ) {
492494 $ result .= $ this ->origTokens ->getTokenCode ($ pos , count ($ origTokens ), 0 );
493495 } else {
@@ -568,9 +570,8 @@ protected function p(Node $node, $parentFormatPreserved = false) : string {
568570 if (is_array ($ subNode ) && is_array ($ origSubNode )) {
569571 // Array subnode changed, we might be able to reconstruct it
570572 $ listResult = $ this ->pArray (
571- $ subNode , $ origSubNode , $ pos , $ indentAdjustment , $ subNodeName ,
572- $ fixupInfo [$ subNodeName ] ?? null ,
573- $ this ->listInsertionMap [$ type . '-> ' . $ subNodeName ] ?? null
573+ $ subNode , $ origSubNode , $ pos , $ indentAdjustment , $ type , $ subNodeName ,
574+ $ fixupInfo [$ subNodeName ] ?? null
574575 );
575576 if (null === $ listResult ) {
576577 return $ this ->pFallback ($ fallbackNode );
@@ -689,18 +690,21 @@ protected function p(Node $node, $parentFormatPreserved = false) : string {
689690 * @param array $origNodes Original nodes
690691 * @param int $pos Current token position (updated by reference)
691692 * @param int $indentAdjustment Adjustment for indentation
693+ * @param string $parentNodeType Type of the containing node.
692694 * @param string $subNodeName Name of array subnode.
693695 * @param null|int $fixup Fixup information for array item nodes
694- * @param null|string $insertStr Separator string to use for insertions
695696 *
696697 * @return null|string Result of pretty print or null if cannot preserve formatting
697698 */
698699 protected function pArray (
699700 array $ nodes , array $ origNodes , int &$ pos , int $ indentAdjustment ,
700- string $ subNodeName , $ fixup , $ insertStr
701+ string $ parentNodeType , string $ subNodeName , $ fixup
701702 ) {
702703 $ diff = $ this ->nodeListDiffer ->diffWithReplacements ($ origNodes , $ nodes );
703704
705+ $ mapKey = $ parentNodeType . '-> ' . $ subNodeName ;
706+ $ insertStr = $ this ->listInsertionMap [$ mapKey ] ?? null ;
707+
704708 $ beforeFirstKeepOrReplace = true ;
705709 $ delayedAdd = [];
706710 $ lastElemIndentLevel = $ this ->indentLevel ;
@@ -874,7 +878,27 @@ protected function pArray(
874878
875879 if (!empty ($ delayedAdd )) {
876880 // TODO Handle insertion into empty list
877- return null ;
881+ if (!isset ($ this ->emptyListInsertionMap [$ mapKey ])) {
882+ return null ;
883+ }
884+
885+ list ($ findToken , $ extraLeft , $ extraRight ) = $ this ->emptyListInsertionMap [$ mapKey ];
886+ if (null !== $ findToken ) {
887+ $ insertPos = $ this ->origTokens ->findRight ($ pos , $ findToken ) + 1 ;
888+ $ result .= $ this ->origTokens ->getTokenCode ($ pos , $ insertPos , $ indentAdjustment );
889+ $ pos = $ insertPos ;
890+ }
891+
892+ $ first = true ;
893+ $ result .= $ extraLeft ;
894+ foreach ($ delayedAdd as $ delayedAddNode ) {
895+ if (!$ first ) {
896+ $ result .= $ insertStr ;
897+ }
898+ $ result .= $ this ->p ($ delayedAddNode , true );
899+ $ first = false ;
900+ }
901+ $ result .= $ extraRight ;
878902 }
879903
880904 return $ result ;
@@ -1229,6 +1253,7 @@ protected function initializeInsertionMap() {
12291253 if ($ this ->insertionMap ) return ;
12301254
12311255 // TODO: "yield" where both key and value are inserted doesn't work
1256+ // [$find, $beforeToken, $extraLeft, $extraRight]
12321257 $ this ->insertionMap = [
12331258 'Expr_ArrayDimFetch->dim ' => ['[ ' , false , null , null ],
12341259 'Expr_ArrayItem->key ' => [null , false , null , ' => ' ],
@@ -1330,6 +1355,59 @@ protected function initializeListInsertionMap() {
13301355 'Stmt_TraitUse->adaptations ' => "\n" ,
13311356 'Stmt_TryCatch->stmts ' => "\n" ,
13321357 'Stmt_While->stmts ' => "\n" ,
1358+
1359+ // dummy for top-level context
1360+ 'File->stmts ' => "\n" ,
1361+ ];
1362+ }
1363+
1364+ protected function initializeEmptyListInsertionMap () {
1365+ if ($ this ->emptyListInsertionMap ) return ;
1366+
1367+ // TODO Insertion into empty statement lists.
1368+
1369+ // [$find, $extraLeft, $extraRight]
1370+ $ this ->emptyListInsertionMap = [
1371+ 'Expr_ArrowFunction->params ' => ['( ' , '' , '' ],
1372+ 'Expr_Closure->uses ' => [') ' , ' use( ' , ') ' ],
1373+ 'Expr_Closure->params ' => ['( ' , '' , '' ],
1374+ 'Expr_FuncCall->args ' => ['( ' , '' , '' ],
1375+ 'Expr_MethodCall->args ' => ['( ' , '' , '' ],
1376+ 'Expr_New->args ' => ['( ' , '' , '' ],
1377+ 'Expr_PrintableNewAnonClass->args ' => ['( ' , '' , '' ],
1378+ 'Expr_PrintableNewAnonClass->implements ' => [null , ' implements ' , '' ],
1379+ 'Expr_StaticCall->args ' => ['( ' , '' , '' ],
1380+ 'Stmt_Class->implements ' => [null , ' implements ' , '' ],
1381+ 'Stmt_ClassMethod->params ' => ['( ' , '' , '' ],
1382+ 'Stmt_Interface->extends ' => [null , ' extends ' , '' ],
1383+ 'Stmt_Function->params ' => ['( ' , '' , '' ],
1384+
1385+ /* These cannot be empty to start with:
1386+ * Expr_Isset->vars
1387+ * Stmt_Catch->types
1388+ * Stmt_Const->consts
1389+ * Stmt_ClassConst->consts
1390+ * Stmt_Declare->declares
1391+ * Stmt_Echo->exprs
1392+ * Stmt_Global->vars
1393+ * Stmt_GroupUse->uses
1394+ * Stmt_Property->props
1395+ * Stmt_StaticVar->vars
1396+ * Stmt_TraitUse->traits
1397+ * Stmt_TraitUseAdaptation_Precedence->insteadof
1398+ * Stmt_Unset->vars
1399+ * Stmt_Use->uses
1400+ */
1401+
1402+ /* TODO
1403+ * Stmt_If->elseifs
1404+ * Stmt_TryCatch->catches
1405+ * Expr_Array->items
1406+ * Expr_List->items
1407+ * Stmt_For->init
1408+ * Stmt_For->cond
1409+ * Stmt_For->loop
1410+ */
13331411 ];
13341412 }
13351413
0 commit comments