From 602af9060d8b71286b50dcdb7a12ec70333efc9d Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sun, 9 Feb 2020 16:53:46 +0100 Subject: [PATCH] Add end line / file position / token position to comments --- lib/PhpParser/Comment.php | 106 ++++++++++++++++++++---- lib/PhpParser/JsonDecoder.php | 4 +- lib/PhpParser/Lexer.php | 16 ++-- lib/PhpParser/PrettyPrinterAbstract.php | 2 +- test/PhpParser/CommentTest.php | 11 ++- test/PhpParser/LexerTest.php | 16 ++-- test/PhpParser/NodeAbstractTest.php | 10 ++- test/PhpParser/ParserTest.php | 9 +- 8 files changed, 138 insertions(+), 36 deletions(-) diff --git a/lib/PhpParser/Comment.php b/lib/PhpParser/Comment.php index 5da8420..61e98d3 100644 --- a/lib/PhpParser/Comment.php +++ b/lib/PhpParser/Comment.php @@ -5,9 +5,12 @@ namespace PhpParser; class Comment implements \JsonSerializable { protected $text; - protected $line; - protected $filePos; - protected $tokenPos; + protected $startLine; + protected $startFilePos; + protected $startTokenPos; + protected $endLine; + protected $endFilePos; + protected $endTokenPos; /** * Constructs a comment node. @@ -18,12 +21,17 @@ class Comment implements \JsonSerializable * @param int $startTokenPos Token offset the comment started on */ public function __construct( - string $text, int $startLine = -1, int $startFilePos = -1, int $startTokenPos = -1 + string $text, + int $startLine = -1, int $startFilePos = -1, int $startTokenPos = -1, + int $endLine = -1, int $endFilePos = -1, int $endTokenPos = -1 ) { $this->text = $text; - $this->line = $startLine; - $this->filePos = $startFilePos; - $this->tokenPos = $startTokenPos; + $this->startLine = $startLine; + $this->startFilePos = $startFilePos; + $this->startTokenPos = $startTokenPos; + $this->endLine = $endLine; + $this->endFilePos = $endFilePos; + $this->endTokenPos = $endTokenPos; } /** @@ -38,28 +46,88 @@ class Comment implements \JsonSerializable /** * Gets the line number the comment started on. * - * @return int Line number + * @return int Line number (or -1 if not available) */ - public function getLine() : int { - return $this->line; + public function getStartLine() : int { + return $this->startLine; } /** * Gets the file offset the comment started on. * - * @return int File offset + * @return int File offset (or -1 if not available) */ - public function getFilePos() : int { - return $this->filePos; + public function getStartFilePos() : int { + return $this->startFilePos; } /** * Gets the token offset the comment started on. * + * @return int Token offset (or -1 if not available) + */ + public function getStartTokenPos() : int { + return $this->startTokenPos; + } + + /** + * Gets the line number the comment ends on. + * + * @return int Line number (or -1 if not available) + */ + public function getEndLine() : int { + return $this->endLine; + } + + /** + * Gets the file offset the comment ends on. + * + * @return int File offset (or -1 if not available) + */ + public function getEndFilePos() : int { + return $this->endFilePos; + } + + /** + * Gets the token offset the comment ends on. + * + * @return int Token offset (or -1 if not available) + */ + public function getEndTokenPos() : int { + return $this->endTokenPos; + } + + /** + * Gets the line number the comment started on. + * + * @deprecated Use getStartLine() instead + * + * @return int Line number + */ + public function getLine() : int { + return $this->startLine; + } + + /** + * Gets the file offset the comment started on. + * + * @deprecated Use getStartFilePos() instead + * + * @return int File offset + */ + public function getFilePos() : int { + return $this->startFilePos; + } + + /** + * Gets the token offset the comment started on. + * + * @deprecated Use getStartTokenPos() instead + * * @return int Token offset */ public function getTokenPos() : int { - return $this->tokenPos; + return $this->startTokenPos; } /** @@ -159,9 +227,13 @@ class Comment implements \JsonSerializable return [ 'nodeType' => $type, 'text' => $this->text, - 'line' => $this->line, - 'filePos' => $this->filePos, - 'tokenPos' => $this->tokenPos, + // TODO: Rename these to include "start". + 'line' => $this->startLine, + 'filePos' => $this->startFilePos, + 'tokenPos' => $this->startTokenPos, + 'endLine' => $this->endLine, + 'endFilePos' => $this->endFilePos, + 'endTokenPos' => $this->endTokenPos, ]; } } diff --git a/lib/PhpParser/JsonDecoder.php b/lib/PhpParser/JsonDecoder.php index 25d1c6a..b55af5b 100644 --- a/lib/PhpParser/JsonDecoder.php +++ b/lib/PhpParser/JsonDecoder.php @@ -73,7 +73,9 @@ class JsonDecoder } return new $className( - $value['text'], $value['line'] ?? -1, $value['filePos'] ?? -1, $value['tokenPos'] ?? -1 + $value['text'], + $value['line'] ?? -1, $value['filePos'] ?? -1, $value['tokenPos'] ?? -1, + $value['endLine'] ?? -1, $value['endFilePos'] ?? -1, $value['endTokenPos'] ?? -1, ); } diff --git a/lib/PhpParser/Lexer.php b/lib/PhpParser/Lexer.php index 012c1f9..694a848 100644 --- a/lib/PhpParser/Lexer.php +++ b/lib/PhpParser/Lexer.php @@ -300,17 +300,23 @@ class Lexer $this->line += substr_count($value, "\n"); $this->filePos += \strlen($value); } else { + $origLine = $this->line; + $origFilePos = $this->filePos; + $this->line += substr_count($token[1], "\n"); + $this->filePos += \strlen($token[1]); + if (\T_COMMENT === $token[0] || \T_DOC_COMMENT === $token[0]) { if ($this->attributeCommentsUsed) { $comment = \T_DOC_COMMENT === $token[0] - ? new Comment\Doc($token[1], $this->line, $this->filePos, $this->pos) - : new Comment($token[1], $this->line, $this->filePos, $this->pos); + ? new Comment\Doc($token[1], + $origLine, $origFilePos, $this->pos, + $this->line, $this->filePos - 1, $this->pos) + : new Comment($token[1], + $origLine, $origFilePos, $this->pos, + $this->line, $this->filePos - 1, $this->pos); $startAttributes['comments'][] = $comment; } } - - $this->line += substr_count($token[1], "\n"); - $this->filePos += \strlen($token[1]); continue; } diff --git a/lib/PhpParser/PrettyPrinterAbstract.php b/lib/PhpParser/PrettyPrinterAbstract.php index 0af858f..05d9ba1 100644 --- a/lib/PhpParser/PrettyPrinterAbstract.php +++ b/lib/PhpParser/PrettyPrinterAbstract.php @@ -762,7 +762,7 @@ abstract class PrettyPrinterAbstract $comments = $arrItem->getComments(); $origComments = $origArrItem->getComments(); - $commentStartPos = $origComments ? $origComments[0]->getTokenPos() : $itemStartPos; + $commentStartPos = $origComments ? $origComments[0]->getStartTokenPos() : $itemStartPos; \assert($commentStartPos >= 0); $commentsChanged = $comments !== $origComments; diff --git a/test/PhpParser/CommentTest.php b/test/PhpParser/CommentTest.php index 409841a..c51a26b 100644 --- a/test/PhpParser/CommentTest.php +++ b/test/PhpParser/CommentTest.php @@ -4,14 +4,21 @@ namespace PhpParser; class CommentTest extends \PHPUnit\Framework\TestCase { - public function testGetSet() { - $comment = new Comment('/* Some comment */', 1, 10, 2); + public function testGetters() { + $comment = new Comment('/* Some comment */', + 1, 10, 2, 1, 27, 2); $this->assertSame('/* Some comment */', $comment->getText()); $this->assertSame('/* Some comment */', (string) $comment); $this->assertSame(1, $comment->getLine()); $this->assertSame(10, $comment->getFilePos()); $this->assertSame(2, $comment->getTokenPos()); + $this->assertSame(1, $comment->getStartLine()); + $this->assertSame(10, $comment->getStartFilePos()); + $this->assertSame(2, $comment->getStartTokenPos()); + $this->assertSame(1, $comment->getEndLine()); + $this->assertSame(27, $comment->getEndFilePos()); + $this->assertSame(2, $comment->getEndTokenPos()); } /** diff --git a/test/PhpParser/LexerTest.php b/test/PhpParser/LexerTest.php index f24c64d..9c34960 100644 --- a/test/PhpParser/LexerTest.php +++ b/test/PhpParser/LexerTest.php @@ -103,7 +103,9 @@ class LexerTest extends \PHPUnit\Framework\TestCase [ 'startLine' => 3, 'comments' => [ - new Comment\Doc('/** doc' . "\n" . 'comment */', 2, 14, 5), + new Comment\Doc('/** doc' . "\n" . 'comment */', + 2, 14, 5, + 3, 31, 5), ] ], ['endLine' => 3] @@ -120,10 +122,14 @@ class LexerTest extends \PHPUnit\Framework\TestCase [ 'startLine' => 2, 'comments' => [ - new Comment('/* comment */', 1, 6, 1), - new Comment('// comment' . "\n", 1, 20, 3), - new Comment\Doc('/** docComment 1 */', 2, 31, 4), - new Comment\Doc('/** docComment 2 */', 2, 50, 5), + new Comment('/* comment */', + 1, 6, 1, 1, 18, 1), + new Comment('// comment' . "\n", + 1, 20, 3, 2, 30, 3), + new Comment\Doc('/** docComment 1 */', + 2, 31, 4, 2, 49, 4), + new Comment\Doc('/** docComment 2 */', + 2, 50, 5, 2, 68, 5), ], ], ['endLine' => 2] diff --git a/test/PhpParser/NodeAbstractTest.php b/test/PhpParser/NodeAbstractTest.php index 071b8f2..57819c9 100644 --- a/test/PhpParser/NodeAbstractTest.php +++ b/test/PhpParser/NodeAbstractTest.php @@ -301,14 +301,20 @@ PHP; "text": "\/\/ comment\n", "line": 2, "filePos": 6, - "tokenPos": 1 + "tokenPos": 1, + "endLine": 3, + "endFilePos": 16, + "endTokenPos": 1 }, { "nodeType": "Comment_Doc", "text": "\/** doc comment *\/", "line": 3, "filePos": 17, - "tokenPos": 2 + "tokenPos": 2, + "endLine": 3, + "endFilePos": 34, + "endTokenPos": 2 } ], "endLine": 6 diff --git a/test/PhpParser/ParserTest.php b/test/PhpParser/ParserTest.php index 0f7f1b7..fc6e53f 100644 --- a/test/PhpParser/ParserTest.php +++ b/test/PhpParser/ParserTest.php @@ -60,7 +60,8 @@ EOC; $this->assertInstanceOf(Stmt\Function_::class, $fn); $this->assertEquals([ 'comments' => [ - new Comment\Doc('/** Doc comment */', 2, 6, 1), + new Comment\Doc('/** Doc comment */', + 2, 6, 1, 2, 23, 1), ], 'startLine' => 3, 'endLine' => 7, @@ -82,8 +83,10 @@ EOC; $this->assertInstanceOf(Stmt\Echo_::class, $echo); $this->assertEquals([ 'comments' => [ - new Comment("// Line\n", 4, 49, 12), - new Comment("// Comments\n", 5, 61, 14), + new Comment("// Line\n", + 4, 49, 12, 5, 56, 12), + new Comment("// Comments\n", + 5, 61, 14, 6, 72, 14), ], 'startLine' => 6, 'endLine' => 6,