Created
December 23, 2012 19:16
-
-
Save anonymous/4365484 to your computer and use it in GitHub Desktop.
First go at a pretty printer that tries to preserve formatting
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class PrettyPrinterTryingToPreserveFormatting extends PHPParser_PrettyPrinter_Zend { | |
/** @var TokenArray */ | |
protected $tokens; | |
protected $indentationStack; | |
protected $indentationStackPos; | |
protected $additionalIndentation; | |
/** | |
* $tokens is expected to have indentation normalized to four spaces. | |
*/ | |
public function prettyPrint2(array $nodes, TokenArray $tokens) { | |
$this->tokens = $tokens; | |
$this->indentationStack = array(0); | |
$this->indentationStackPos = 0; | |
return parent::prettyPrint($nodes); | |
} | |
protected function pIndent($string, $levels) { | |
$string = preg_replace( | |
'~\n(?!$|' . $this->noIndentToken . ')~', | |
"\n" . str_repeat(' ', $levels), | |
$string | |
); | |
return $string; | |
} | |
protected function p(PHPParser_Node $node) { | |
$changed = $node->hasAttribute('changed') || !$node->hasAttribute('startOffset'); | |
if (!$changed) { | |
$curStartOffset = $node->getAttribute('startOffset'); | |
$indentation = $this->getIndentation($curStartOffset); | |
$this->indentationStack[$this->indentationStackPos + 1] = $indentation; | |
$this->indentationStackPos++; | |
$result = ''; | |
/* We need to iterate SELF_FIRST because RAI does not consider objects to be leafs */ | |
$iterator = new RecursiveIteratorIterator( | |
new RecursiveArrayIterator($node->getSubNodes()), | |
RecursiveIteratorIterator::SELF_FIRST | |
); | |
foreach ($iterator as $subNode) { | |
if (!$subNode instanceof PHPParser_Node) continue; | |
if (!$subNode->hasAttribute('startOffset') || !$subNode->hasAttribute('endOffset')) { | |
// Resort to normal pretty printing | |
$this->indentationStackPos--; | |
return $this->{'p' . $node->getType()}($node); | |
} | |
$curEndOffset = $subNode->getAttribute('startOffset') - 1; | |
$result .= $this->handleTokens($indentation, $curStartOffset, $curEndOffset); | |
$result .= $this->pIndent( | |
$this->p($subNode), | |
$this->indentationStack[$this->indentationStackPos] - $indentation | |
); | |
$curStartOffset = $subNode->getAttribute('endOffset') + 1; | |
} | |
$curEndOffset = $node->getAttribute('endOffset'); | |
$result .= $this->handleTokens($indentation, $curStartOffset, $curEndOffset); | |
$this->indentationStackPos--; | |
return $result; | |
} else { | |
return $this->{'p' . $node->getType()}($node); | |
} | |
} | |
protected function getIndentation($offset) { | |
for ($i = $offset; $i >= 0; --$i) { | |
$token = $this->tokens->get($i); | |
if (!$token->hasType(T_WHITESPACE)) continue; | |
$tokenContent = $token->getContent(); | |
if (false !== $newlinePos = strrpos($tokenContent, "\n")) { | |
return substr_count($tokenContent, ' ', $newlinePos + 1); | |
} | |
$prevToken = $this->tokens->get($i - 1); | |
if (!$prevToken->hasType(T_COMMENT) || !$prevToken->endsWith("\n")) { | |
continue; | |
} | |
return substr_count($tokenContent, ' '); | |
} | |
return 0; | |
} | |
protected function handleTokens($baseIndentation, $startOffset, $endOffset) { | |
$result = ''; | |
for ($i = $startOffset; $i <= $endOffset; ++$i) { | |
$token = $this->tokens->get($i); | |
$tokenContent = $token->getContent(); | |
if ($token->hasType(T_WHITESPACE)) { | |
if (false !== $newlinePos = strrpos($tokenContent, "\n")) { | |
$this->indentationStack[$this->indentationStackPos] = substr_count( | |
$tokenContent, ' ', $newlinePos + 1 | |
); | |
$result .= str_replace("\n" . str_repeat(' ', $baseIndentation), "\n", $tokenContent); | |
continue; | |
} | |
$prevToken = $this->tokens->get($i - 1); | |
if ($prevToken->hasType(T_COMMENT) && $prevToken->endsWith("\n")) { | |
$n = $this->indentationStack[$this->indentationStackPos] = substr_count($tokenContent, ' '); | |
$result .= substr($tokenContent, $n * 4); | |
continue; | |
} | |
} | |
$result .= $tokenContent; | |
} | |
return $result; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
interface Token { | |
public function hasType($type); | |
public function getContent(); | |
public function endsWith($string); | |
} | |
abstract class TokenAbstract implements Token { | |
public function endsWith($string) { | |
$length = strlen($string); | |
if (0 === $length) return true; | |
return $string === substr($this->getContent(), -$length); | |
} | |
} | |
class DummyTokenException extends Exception {} | |
class DummyToken extends TokenAbstract { | |
public function hasType($type) { | |
return false; | |
} | |
public function getContent() { | |
throw new DummyTokenException; | |
} | |
} | |
class CharToken extends TokenAbstract { | |
protected $char; | |
public function __construct($char) { | |
$this->char = $char; | |
} | |
public function hasType($type) { | |
return $type === $this->char; | |
} | |
public function getContent() { | |
return $this->char; | |
} | |
} | |
class ArrayToken extends TokenAbstract { | |
protected $type; | |
protected $content; | |
protected $startLine; | |
public function __construct($token) { | |
list($this->type, $this->content, $this->startLine) = $token; | |
} | |
public function hasType($type) { | |
return $type === $this->type; | |
} | |
public function getContent() { | |
return $this->content; | |
} | |
} | |
class TokenArray { | |
protected $tokens; | |
public function __construct(array $tokens) { | |
$this->setTokens($tokens); | |
} | |
protected function setTokens(array $tokens) { | |
$this->tokens = array(); | |
foreach ($tokens as $token) { | |
if (is_array($token)) { | |
$this->tokens[] = new ArrayToken($token); | |
} else { | |
$this->tokens[] = new CharToken($token); | |
} | |
} | |
} | |
public function get($offset) { | |
return isset($this->tokens[$offset]) ? $this->tokens[$offset] : new DummyToken; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment