Replace noIndent mechanism in pretty printer

Instead store newline with indentation in $nl property and use it
where needed.

The implementation should be changed to compilet indentLevel and
fpIndentLevel, I don't think these need to be kept separate.
This commit is contained in:
Nikita Popov 2017-09-03 17:05:35 +02:00
parent 31065389f1
commit 61e624bc9d
2 changed files with 95 additions and 73 deletions

View File

@ -102,24 +102,24 @@ class Standard extends PrettyPrinterAbstract
$label = $node->getAttribute('docLabel');
if ($label && !$this->containsEndLabel($node->value, $label)) {
if ($node->value === '') {
return $this->pNoIndent("<<<'$label'\n$label") . $this->docStringEndToken;
return "<<<'$label'\n$label" . $this->docStringEndToken;
}
return $this->pNoIndent("<<<'$label'\n$node->value\n$label")
return "<<<'$label'\n$node->value\n$label"
. $this->docStringEndToken;
}
/* break missing intentionally */
case Scalar\String_::KIND_SINGLE_QUOTED:
return '\'' . $this->pNoIndent(addcslashes($node->value, '\'\\')) . '\'';
return '\'' . addcslashes($node->value, '\'\\') . '\'';
case Scalar\String_::KIND_HEREDOC:
$label = $node->getAttribute('docLabel');
if ($label && !$this->containsEndLabel($node->value, $label)) {
if ($node->value === '') {
return $this->pNoIndent("<<<$label\n$label") . $this->docStringEndToken;
return "<<<$label\n$label" . $this->docStringEndToken;
}
$escaped = $this->escapeString($node->value, null);
return $this->pNoIndent("<<<$label\n" . $escaped . "\n$label")
return "<<<$label\n" . $escaped . "\n$label"
. $this->docStringEndToken;
}
/* break missing intentionally */
@ -137,12 +137,11 @@ class Standard extends PrettyPrinterAbstract
&& $node->parts[0] instanceof Scalar\EncapsedStringPart
&& $node->parts[0]->value === ''
) {
return $this->pNoIndent("<<<$label\n$label") . $this->docStringEndToken;
return "<<<$label\n$label" . $this->docStringEndToken;
}
return $this->pNoIndent(
"<<<$label\n" . $this->pEncapsList($node->parts, null) . "\n$label"
) . $this->docStringEndToken;
return "<<<$label\n" . $this->pEncapsList($node->parts, null) . "\n$label"
. $this->docStringEndToken;
}
}
return '"' . $this->pEncapsList($node->parts, '"') . '"';
@ -556,7 +555,7 @@ class Standard extends PrettyPrinterAbstract
. '(' . $this->pCommaSeparated($node->params) . ')'
. (!empty($node->uses) ? ' use(' . $this->pCommaSeparated($node->uses) . ')': '')
. (null !== $node->returnType ? ' : ' . $this->p($node->returnType) : '')
. ' {' . $this->pStmts($node->stmts) . "\n" . '}';
. ' {' . $this->pStmts($node->stmts) . $this->nl . '}';
}
protected function pExpr_ClosureUse(Expr\ClosureUse $node) {
@ -605,10 +604,11 @@ class Standard extends PrettyPrinterAbstract
protected function pStmt_Namespace(Stmt\Namespace_ $node) {
if ($this->canUseSemicolonNamespaces) {
return 'namespace ' . $this->p($node->name) . ';' . "\n" . $this->pStmts($node->stmts, false);
return 'namespace ' . $this->p($node->name) . ';'
. $this->nl . $this->pStmts($node->stmts, false);
} else {
return 'namespace' . (null !== $node->name ? ' ' . $this->p($node->name) : '')
. ' {' . $this->pStmts($node->stmts) . "\n" . '}';
. ' {' . $this->pStmts($node->stmts) . $this->nl . '}';
}
}
@ -635,7 +635,7 @@ class Standard extends PrettyPrinterAbstract
protected function pStmt_Interface(Stmt\Interface_ $node) {
return 'interface ' . $node->name
. (!empty($node->extends) ? ' extends ' . $this->pCommaSeparated($node->extends) : '')
. "\n" . '{' . $this->pStmts($node->stmts) . "\n" . '}';
. $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
}
protected function pStmt_Class(Stmt\Class_ $node) {
@ -644,14 +644,14 @@ class Standard extends PrettyPrinterAbstract
protected function pStmt_Trait(Stmt\Trait_ $node) {
return 'trait ' . $node->name
. "\n" . '{' . $this->pStmts($node->stmts) . "\n" . '}';
. $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
}
protected function pStmt_TraitUse(Stmt\TraitUse $node) {
return 'use ' . $this->pCommaSeparated($node->traits)
. (empty($node->adaptations)
? ';'
: ' {' . $this->pStmts($node->adaptations) . "\n" . '}');
: ' {' . $this->pStmts($node->adaptations) . $this->nl . '}');
}
protected function pStmt_TraitUseAdaptation_Precedence(Stmt\TraitUseAdaptation\Precedence $node) {
@ -682,7 +682,7 @@ class Standard extends PrettyPrinterAbstract
. '(' . $this->pCommaSeparated($node->params) . ')'
. (null !== $node->returnType ? ' : ' . $this->p($node->returnType) : '')
. (null !== $node->stmts
? "\n" . '{' . $this->pStmts($node->stmts) . "\n" . '}'
? $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'
: ';');
}
@ -695,7 +695,7 @@ class Standard extends PrettyPrinterAbstract
return 'function ' . ($node->byRef ? '&' : '') . $node->name
. '(' . $this->pCommaSeparated($node->params) . ')'
. (null !== $node->returnType ? ' : ' . $this->p($node->returnType) : '')
. "\n" . '{' . $this->pStmts($node->stmts) . "\n" . '}';
. $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
}
protected function pStmt_Const(Stmt\Const_ $node) {
@ -704,7 +704,7 @@ class Standard extends PrettyPrinterAbstract
protected function pStmt_Declare(Stmt\Declare_ $node) {
return 'declare (' . $this->pCommaSeparated($node->declares) . ')'
. (null !== $node->stmts ? ' {' . $this->pStmts($node->stmts) . "\n" . '}' : ';');
. (null !== $node->stmts ? ' {' . $this->pStmts($node->stmts) . $this->nl . '}' : ';');
}
protected function pStmt_DeclareDeclare(Stmt\DeclareDeclare $node) {
@ -715,18 +715,18 @@ class Standard extends PrettyPrinterAbstract
protected function pStmt_If(Stmt\If_ $node) {
return 'if (' . $this->p($node->cond) . ') {'
. $this->pStmts($node->stmts) . "\n" . '}'
. $this->pStmts($node->stmts) . $this->nl . '}'
. $this->pImplode($node->elseifs)
. (null !== $node->else ? $this->p($node->else) : '');
}
protected function pStmt_ElseIf(Stmt\ElseIf_ $node) {
return ' elseif (' . $this->p($node->cond) . ') {'
. $this->pStmts($node->stmts) . "\n" . '}';
. $this->pStmts($node->stmts) . $this->nl . '}';
}
protected function pStmt_Else(Stmt\Else_ $node) {
return ' else {' . $this->pStmts($node->stmts) . "\n" . '}';
return ' else {' . $this->pStmts($node->stmts) . $this->nl . '}';
}
protected function pStmt_For(Stmt\For_ $node) {
@ -734,29 +734,29 @@ class Standard extends PrettyPrinterAbstract
. $this->pCommaSeparated($node->init) . ';' . (!empty($node->cond) ? ' ' : '')
. $this->pCommaSeparated($node->cond) . ';' . (!empty($node->loop) ? ' ' : '')
. $this->pCommaSeparated($node->loop)
. ') {' . $this->pStmts($node->stmts) . "\n" . '}';
. ') {' . $this->pStmts($node->stmts) . $this->nl . '}';
}
protected function pStmt_Foreach(Stmt\Foreach_ $node) {
return 'foreach (' . $this->p($node->expr) . ' as '
. (null !== $node->keyVar ? $this->p($node->keyVar) . ' => ' : '')
. ($node->byRef ? '&' : '') . $this->p($node->valueVar) . ') {'
. $this->pStmts($node->stmts) . "\n" . '}';
. $this->pStmts($node->stmts) . $this->nl . '}';
}
protected function pStmt_While(Stmt\While_ $node) {
return 'while (' . $this->p($node->cond) . ') {'
. $this->pStmts($node->stmts) . "\n" . '}';
. $this->pStmts($node->stmts) . $this->nl . '}';
}
protected function pStmt_Do(Stmt\Do_ $node) {
return 'do {' . $this->pStmts($node->stmts) . "\n"
return 'do {' . $this->pStmts($node->stmts) . $this->nl
. '} while (' . $this->p($node->cond) . ');';
}
protected function pStmt_Switch(Stmt\Switch_ $node) {
return 'switch (' . $this->p($node->cond) . ') {'
. $this->pStmts($node->cases) . "\n" . '}';
. $this->pStmts($node->cases) . $this->nl . '}';
}
protected function pStmt_Case(Stmt\Case_ $node) {
@ -765,7 +765,7 @@ class Standard extends PrettyPrinterAbstract
}
protected function pStmt_TryCatch(Stmt\TryCatch $node) {
return 'try {' . $this->pStmts($node->stmts) . "\n" . '}'
return 'try {' . $this->pStmts($node->stmts) . $this->nl . '}'
. ($node->catches ? ' ' . $this->pImplode($node->catches, ' ') : '')
. ($node->finally !== null ? ' ' . $this->p($node->finally) : '');
}
@ -773,11 +773,11 @@ class Standard extends PrettyPrinterAbstract
protected function pStmt_Catch(Stmt\Catch_ $node) {
return 'catch (' . $this->pImplode($node->types, '|') . ' '
. $this->p($node->var)
. ') {' . $this->pStmts($node->stmts) . "\n" . '}';
. ') {' . $this->pStmts($node->stmts) . $this->nl . '}';
}
protected function pStmt_Finally(Stmt\Finally_ $node) {
return 'finally {' . $this->pStmts($node->stmts) . "\n" . '}';
return 'finally {' . $this->pStmts($node->stmts) . $this->nl . '}';
}
protected function pStmt_Break(Stmt\Break_ $node) {
@ -832,8 +832,8 @@ class Standard extends PrettyPrinterAbstract
}
protected function pStmt_InlineHTML(Stmt\InlineHTML $node) {
$newline = $node->getAttribute('hasLeadingNewline', true) ? "\n" : '';
return '?>' . $this->pNoIndent($newline . $node->value) . '<?php ';
$newline = $node->getAttribute('hasLeadingNewline', true) ? $this->nl : '';
return '?>' . $newline . $node->value . '<?php ';
}
protected function pStmt_HaltCompiler(Stmt\HaltCompiler $node) {
@ -851,7 +851,7 @@ class Standard extends PrettyPrinterAbstract
. 'class' . $afterClassToken
. (null !== $node->extends ? ' extends ' . $this->p($node->extends) : '')
. (!empty($node->implements) ? ' implements ' . $this->pCommaSeparated($node->implements) : '')
. "\n" . '{' . $this->pStmts($node->stmts) . "\n" . '}';
. $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
}
protected function pObjectProperty($node) {
@ -952,7 +952,7 @@ class Standard extends PrettyPrinterAbstract
if (!$this->hasNodeWithComments($nodes)) {
return $this->pCommaSeparated($nodes);
} else {
return $this->pCommaSeparatedMultiline($nodes, $trailingComma) . "\n";
return $this->pCommaSeparatedMultiline($nodes, $trailingComma) . $this->nl;
}
}
}

View File

@ -84,8 +84,10 @@ abstract class PrettyPrinterAbstract
'Expr_Include' => [200, -1],
];
/** @var string Token placed before newline to ensure it is not indented. */
protected $noIndentToken;
/** @var int Current indentation level. */
protected $indentLevel;
/** @var string Newline including current indentation. */
protected $nl;
/** @var string Token placed at end of doc string to ensure it is followed by a newline. */
protected $docStringEndToken;
/** @var bool Whether semicolon namespaces can be used (i.e. no global namespace is used) */
@ -95,8 +97,8 @@ abstract class PrettyPrinterAbstract
/** @var array Original tokens for use in format-preserving pretty print */
protected $origTokens;
/** @var int Current indentation level during format-preserving pretty pting */
protected $indentLevel;
/** @var int Current indentation level during format-preserving pretty printing */
protected $fpIndentLevel;
/** @var bool[] Map determining whether a certain character is a label character */
protected $labelCharMap;
/**
@ -129,6 +131,30 @@ abstract class PrettyPrinterAbstract
$this->options = $options + $defaultOptions;
}
/**
* Reset pretty printing state.
*/
protected function resetState() {
$this->indentLevel = 0;
$this->nl = "\n";
}
/**
* Increase indentation level.
*/
protected function indent() {
$this->indentLevel += 4;
$this->nl .= ' ';
}
/**
* Decrease indentation level.
*/
protected function outdent() {
$this->indentLevel -= 4;
$this->nl = "\n" . str_repeat(' ', $this->indentLevel);
}
/**
* Pretty prints an array of statements.
*
@ -137,6 +163,7 @@ abstract class PrettyPrinterAbstract
* @return string Pretty printed statements
*/
public function prettyPrint(array $stmts) : string {
$this->resetState();
$this->preprocessNodes($stmts);
return ltrim($this->handleMagicTokens($this->pStmts($stmts, false)));
@ -150,6 +177,7 @@ abstract class PrettyPrinterAbstract
* @return string Pretty printed node
*/
public function prettyPrintExpr(Expr $node) : string {
$this->resetState();
return $this->handleMagicTokens($this->p($node));
}
@ -199,9 +227,6 @@ abstract class PrettyPrinterAbstract
* @return string
*/
protected function handleMagicTokens(string $str) : string {
// Drop no-indent tokens
$str = str_replace($this->noIndentToken, '', $str);
// Replace doc-string-end tokens with nothing or a newline
$str = str_replace($this->docStringEndToken . ";\n", ";\n", $str);
$str = str_replace($this->docStringEndToken, "\n", $str);
@ -218,24 +243,28 @@ abstract class PrettyPrinterAbstract
* @return string Pretty printed statements
*/
protected function pStmts(array $nodes, bool $indent = true) : string {
if ($indent) {
$this->indent();
}
$result = '';
foreach ($nodes as $node) {
$comments = $node->getAttribute('comments', []);
if ($comments) {
$result .= "\n" . $this->pComments($comments);
$result .= $this->nl . $this->pComments($comments);
if ($node instanceof Stmt\Nop) {
continue;
}
}
$result .= "\n" . $this->p($node);
$result .= $this->nl . $this->p($node);
}
if ($indent) {
return preg_replace('~\n(?!$|' . $this->noIndentToken . ')~', "\n ", $result);
} else {
return $result;
$this->outdent();
}
return $result;
}
/**
@ -353,36 +382,28 @@ abstract class PrettyPrinterAbstract
* @return string Comma separated pretty printed nodes in multiline style
*/
protected function pCommaSeparatedMultiline(array $nodes, bool $trailingComma) : string {
$this->indent();
$result = '';
$lastIdx = count($nodes) - 1;
foreach ($nodes as $idx => $node) {
if ($node !== null) {
$comments = $node->getAttribute('comments', []);
if ($comments) {
$result .= "\n" . $this->pComments($comments);
$result .= $this->nl . $this->pComments($comments);
}
$result .= "\n" . $this->p($node);
$result .= $this->nl . $this->p($node);
} else {
$result .= "\n";
$result .= $this->nl;
}
if ($trailingComma || $idx !== $lastIdx) {
$result .= ',';
}
}
return preg_replace('~\n(?!$|' . $this->noIndentToken . ')~', "\n ", $result);
}
/**
* Signals the pretty printer that a string shall not be indented.
*
* @param string $string Not to be indented string
*
* @return string String marked with $this->noIndentToken's.
*/
protected function pNoIndent(string $string) : string {
return str_replace("\n", "\n" . $this->noIndentToken, $string);
$this->outdent();
return $result;
}
/**
@ -396,10 +417,10 @@ abstract class PrettyPrinterAbstract
$formattedComments = [];
foreach ($comments as $comment) {
$formattedComments[] = $comment->getReformattedText();
$formattedComments[] = str_replace("\n", $this->nl, $comment->getReformattedText());
}
return implode("\n", $formattedComments);
return implode($this->nl, $formattedComments);
}
/**
@ -425,8 +446,9 @@ abstract class PrettyPrinterAbstract
$this->initializeRemovalMap();
$this->initializeInsertionMap();
$this->resetState();
$this->origTokens = $origTokens;
$this->indentLevel = 0;
$this->fpIndentLevel = 0;
$this->preprocessNodes($stmts);
@ -486,7 +508,7 @@ abstract class PrettyPrinterAbstract
return $this->pFallback($node);
}
$indentAdjustment = $this->indentLevel - $this->getIndentationBefore($startPos);
$indentAdjustment = $this->fpIndentLevel - $this->getIndentationBefore($startPos);
$type = $node->getType();
$fixupInfo = $this->fixupMap[$type] ?? null;
@ -581,8 +603,8 @@ abstract class PrettyPrinterAbstract
if (null !== $subNode) {
$result .= $extraLeft;
$origIndentLevel = $this->indentLevel;
$this->indentLevel = $this->getIndentationBefore($subStartPos) + $indentAdjustment;
$origIndentLevel = $this->fpIndentLevel;
$this->fpIndentLevel = $this->getIndentationBefore($subStartPos) + $indentAdjustment;
// If it's the same node that was previously in this position, it certainly doesn't
// need fixup. It's important to check this here, because our fixup checks are more
@ -597,7 +619,7 @@ abstract class PrettyPrinterAbstract
}
$this->safeAppend($result, $res);
$this->indentLevel = $origIndentLevel;
$this->fpIndentLevel = $origIndentLevel;
$result .= $extraRight;
}
@ -657,8 +679,8 @@ abstract class PrettyPrinterAbstract
$result .= $this->getTokenCode($pos, $itemStartPos, $indentAdjustment);
$origIndentLevel = $this->indentLevel;
$this->indentLevel = $this->getIndentationBefore($itemStartPos) + $indentAdjustment;
$origIndentLevel = $this->fpIndentLevel;
$this->fpIndentLevel = $this->getIndentationBefore($itemStartPos) + $indentAdjustment;
if (null !== $fixup && $arrItem->getAttribute('origNode') !== $origArrItem) {
$res = $this->pFixup($fixup, $arrItem, null, $itemStartPos, $itemEndPos);
@ -667,7 +689,7 @@ abstract class PrettyPrinterAbstract
}
$this->safeAppend($result, $res);
$this->indentLevel = $origIndentLevel;
$this->fpIndentLevel = $origIndentLevel;
$pos = $itemEndPos + 1;
}
return $result;
@ -884,13 +906,13 @@ abstract class PrettyPrinterAbstract
$type = $token[0];
$content = $token[1];
if ($type === T_CONSTANT_ENCAPSED_STRING || $type === T_ENCAPSED_AND_WHITESPACE) {
$result .= $this->pNoIndent($content);
$result .= $content;
} else {
// TODO Handle non-space indentation
if ($indent < 0) {
$result .= str_replace("\n" . str_repeat(" ", -$indent), "\n", $content);
$result .= str_replace("\n" . str_repeat(" ", -$indent), $this->nl, $content);
} else if ($indent > 0) {
$result .= str_replace("\n", "\n" . str_repeat(" ", $indent), $content);
$result .= str_replace("\n", $this->nl . str_repeat(" ", $indent), $content);
} else {
$result .= $content;
}