@@ -120,7 +120,7 @@ abstract class PrettyPrinterAbstract
120
120
*/
121
121
protected $ removalMap ;
122
122
/**
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].
124
124
* $find is an optional token after which the insertion occurs. $extraLeft/Right
125
125
* are optionally added before/after the main insertions.
126
126
*/
@@ -130,6 +130,7 @@ abstract class PrettyPrinterAbstract
130
130
* between elements of this list subnode.
131
131
*/
132
132
protected $ listInsertionMap ;
133
+ protected $ emptyListInsertionMap ;
133
134
/** @var int[] Map from "{$node->getType()}->{$subNode}" to token before which the modifiers
134
135
* should be reprinted. */
135
136
protected $ modifierChangeMap ;
@@ -479,6 +480,7 @@ public function printFormatPreserving(array $stmts, array $origStmts, array $ori
479
480
$ this ->initializeRemovalMap ();
480
481
$ this ->initializeInsertionMap ();
481
482
$ this ->initializeListInsertionMap ();
483
+ $ this ->initializeEmptyListInsertionMap ();
482
484
$ this ->initializeModifierChangeMap ();
483
485
484
486
$ this ->resetState ();
@@ -487,7 +489,7 @@ public function printFormatPreserving(array $stmts, array $origStmts, array $ori
487
489
$ this ->preprocessNodes ($ stmts );
488
490
489
491
$ pos = 0 ;
490
- $ result = $ this ->pArray ($ stmts , $ origStmts , $ pos , 0 , 'stmts ' , null , "\n" );
492
+ $ result = $ this ->pArray ($ stmts , $ origStmts , $ pos , 0 , 'File ' , ' stmts ' , null );
491
493
if (null !== $ result ) {
492
494
$ result .= $ this ->origTokens ->getTokenCode ($ pos , count ($ origTokens ), 0 );
493
495
} else {
@@ -568,9 +570,8 @@ protected function p(Node $node, $parentFormatPreserved = false) : string {
568
570
if (is_array ($ subNode ) && is_array ($ origSubNode )) {
569
571
// Array subnode changed, we might be able to reconstruct it
570
572
$ 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
574
575
);
575
576
if (null === $ listResult ) {
576
577
return $ this ->pFallback ($ fallbackNode );
@@ -689,18 +690,21 @@ protected function p(Node $node, $parentFormatPreserved = false) : string {
689
690
* @param array $origNodes Original nodes
690
691
* @param int $pos Current token position (updated by reference)
691
692
* @param int $indentAdjustment Adjustment for indentation
693
+ * @param string $parentNodeType Type of the containing node.
692
694
* @param string $subNodeName Name of array subnode.
693
695
* @param null|int $fixup Fixup information for array item nodes
694
- * @param null|string $insertStr Separator string to use for insertions
695
696
*
696
697
* @return null|string Result of pretty print or null if cannot preserve formatting
697
698
*/
698
699
protected function pArray (
699
700
array $ nodes , array $ origNodes , int &$ pos , int $ indentAdjustment ,
700
- string $ subNodeName , $ fixup , $ insertStr
701
+ string $ parentNodeType , string $ subNodeName , $ fixup
701
702
) {
702
703
$ diff = $ this ->nodeListDiffer ->diffWithReplacements ($ origNodes , $ nodes );
703
704
705
+ $ mapKey = $ parentNodeType . '-> ' . $ subNodeName ;
706
+ $ insertStr = $ this ->listInsertionMap [$ mapKey ] ?? null ;
707
+
704
708
$ beforeFirstKeepOrReplace = true ;
705
709
$ delayedAdd = [];
706
710
$ lastElemIndentLevel = $ this ->indentLevel ;
@@ -874,7 +878,27 @@ protected function pArray(
874
878
875
879
if (!empty ($ delayedAdd )) {
876
880
// 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 ;
878
902
}
879
903
880
904
return $ result ;
@@ -1229,6 +1253,7 @@ protected function initializeInsertionMap() {
1229
1253
if ($ this ->insertionMap ) return ;
1230
1254
1231
1255
// TODO: "yield" where both key and value are inserted doesn't work
1256
+ // [$find, $beforeToken, $extraLeft, $extraRight]
1232
1257
$ this ->insertionMap = [
1233
1258
'Expr_ArrayDimFetch->dim ' => ['[ ' , false , null , null ],
1234
1259
'Expr_ArrayItem->key ' => [null , false , null , ' => ' ],
@@ -1330,6 +1355,59 @@ protected function initializeListInsertionMap() {
1330
1355
'Stmt_TraitUse->adaptations ' => "\n" ,
1331
1356
'Stmt_TryCatch->stmts ' => "\n" ,
1332
1357
'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
+ */
1333
1411
];
1334
1412
}
1335
1413
0 commit comments