Add end line / file position / token position to comments

This commit is contained in:
Nikita Popov 2020-02-09 16:53:46 +01:00
parent bf086d9833
commit 602af9060d
8 changed files with 138 additions and 36 deletions

View File

@ -5,9 +5,12 @@ namespace PhpParser;
class Comment implements \JsonSerializable class Comment implements \JsonSerializable
{ {
protected $text; protected $text;
protected $line; protected $startLine;
protected $filePos; protected $startFilePos;
protected $tokenPos; protected $startTokenPos;
protected $endLine;
protected $endFilePos;
protected $endTokenPos;
/** /**
* Constructs a comment node. * Constructs a comment node.
@ -18,12 +21,17 @@ class Comment implements \JsonSerializable
* @param int $startTokenPos Token offset the comment started on * @param int $startTokenPos Token offset the comment started on
*/ */
public function __construct( 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->text = $text;
$this->line = $startLine; $this->startLine = $startLine;
$this->filePos = $startFilePos; $this->startFilePos = $startFilePos;
$this->tokenPos = $startTokenPos; $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. * 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 { public function getStartLine() : int {
return $this->line; return $this->startLine;
} }
/** /**
* Gets the file offset the comment started on. * 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 { public function getStartFilePos() : int {
return $this->filePos; return $this->startFilePos;
} }
/** /**
* Gets the token offset the comment started on. * 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 * @return int Token offset
*/ */
public function getTokenPos() : int { public function getTokenPos() : int {
return $this->tokenPos; return $this->startTokenPos;
} }
/** /**
@ -159,9 +227,13 @@ class Comment implements \JsonSerializable
return [ return [
'nodeType' => $type, 'nodeType' => $type,
'text' => $this->text, 'text' => $this->text,
'line' => $this->line, // TODO: Rename these to include "start".
'filePos' => $this->filePos, 'line' => $this->startLine,
'tokenPos' => $this->tokenPos, 'filePos' => $this->startFilePos,
'tokenPos' => $this->startTokenPos,
'endLine' => $this->endLine,
'endFilePos' => $this->endFilePos,
'endTokenPos' => $this->endTokenPos,
]; ];
} }
} }

View File

@ -73,7 +73,9 @@ class JsonDecoder
} }
return new $className( 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,
); );
} }

View File

@ -300,17 +300,23 @@ class Lexer
$this->line += substr_count($value, "\n"); $this->line += substr_count($value, "\n");
$this->filePos += \strlen($value); $this->filePos += \strlen($value);
} else { } 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 (\T_COMMENT === $token[0] || \T_DOC_COMMENT === $token[0]) {
if ($this->attributeCommentsUsed) { if ($this->attributeCommentsUsed) {
$comment = \T_DOC_COMMENT === $token[0] $comment = \T_DOC_COMMENT === $token[0]
? new Comment\Doc($token[1], $this->line, $this->filePos, $this->pos) ? new Comment\Doc($token[1],
: new Comment($token[1], $this->line, $this->filePos, $this->pos); $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; $startAttributes['comments'][] = $comment;
} }
} }
$this->line += substr_count($token[1], "\n");
$this->filePos += \strlen($token[1]);
continue; continue;
} }

View File

@ -762,7 +762,7 @@ abstract class PrettyPrinterAbstract
$comments = $arrItem->getComments(); $comments = $arrItem->getComments();
$origComments = $origArrItem->getComments(); $origComments = $origArrItem->getComments();
$commentStartPos = $origComments ? $origComments[0]->getTokenPos() : $itemStartPos; $commentStartPos = $origComments ? $origComments[0]->getStartTokenPos() : $itemStartPos;
\assert($commentStartPos >= 0); \assert($commentStartPos >= 0);
$commentsChanged = $comments !== $origComments; $commentsChanged = $comments !== $origComments;

View File

@ -4,14 +4,21 @@ namespace PhpParser;
class CommentTest extends \PHPUnit\Framework\TestCase class CommentTest extends \PHPUnit\Framework\TestCase
{ {
public function testGetSet() { public function testGetters() {
$comment = new Comment('/* Some comment */', 1, 10, 2); $comment = new Comment('/* Some comment */',
1, 10, 2, 1, 27, 2);
$this->assertSame('/* Some comment */', $comment->getText()); $this->assertSame('/* Some comment */', $comment->getText());
$this->assertSame('/* Some comment */', (string) $comment); $this->assertSame('/* Some comment */', (string) $comment);
$this->assertSame(1, $comment->getLine()); $this->assertSame(1, $comment->getLine());
$this->assertSame(10, $comment->getFilePos()); $this->assertSame(10, $comment->getFilePos());
$this->assertSame(2, $comment->getTokenPos()); $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());
} }
/** /**

View File

@ -103,7 +103,9 @@ class LexerTest extends \PHPUnit\Framework\TestCase
[ [
'startLine' => 3, 'startLine' => 3,
'comments' => [ 'comments' => [
new Comment\Doc('/** doc' . "\n" . 'comment */', 2, 14, 5), new Comment\Doc('/** doc' . "\n" . 'comment */',
2, 14, 5,
3, 31, 5),
] ]
], ],
['endLine' => 3] ['endLine' => 3]
@ -120,10 +122,14 @@ class LexerTest extends \PHPUnit\Framework\TestCase
[ [
'startLine' => 2, 'startLine' => 2,
'comments' => [ 'comments' => [
new Comment('/* comment */', 1, 6, 1), new Comment('/* comment */',
new Comment('// comment' . "\n", 1, 20, 3), 1, 6, 1, 1, 18, 1),
new Comment\Doc('/** docComment 1 */', 2, 31, 4), new Comment('// comment' . "\n",
new Comment\Doc('/** docComment 2 */', 2, 50, 5), 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] ['endLine' => 2]

View File

@ -301,14 +301,20 @@ PHP;
"text": "\/\/ comment\n", "text": "\/\/ comment\n",
"line": 2, "line": 2,
"filePos": 6, "filePos": 6,
"tokenPos": 1 "tokenPos": 1,
"endLine": 3,
"endFilePos": 16,
"endTokenPos": 1
}, },
{ {
"nodeType": "Comment_Doc", "nodeType": "Comment_Doc",
"text": "\/** doc comment *\/", "text": "\/** doc comment *\/",
"line": 3, "line": 3,
"filePos": 17, "filePos": 17,
"tokenPos": 2 "tokenPos": 2,
"endLine": 3,
"endFilePos": 34,
"endTokenPos": 2
} }
], ],
"endLine": 6 "endLine": 6

View File

@ -60,7 +60,8 @@ EOC;
$this->assertInstanceOf(Stmt\Function_::class, $fn); $this->assertInstanceOf(Stmt\Function_::class, $fn);
$this->assertEquals([ $this->assertEquals([
'comments' => [ 'comments' => [
new Comment\Doc('/** Doc comment */', 2, 6, 1), new Comment\Doc('/** Doc comment */',
2, 6, 1, 2, 23, 1),
], ],
'startLine' => 3, 'startLine' => 3,
'endLine' => 7, 'endLine' => 7,
@ -82,8 +83,10 @@ EOC;
$this->assertInstanceOf(Stmt\Echo_::class, $echo); $this->assertInstanceOf(Stmt\Echo_::class, $echo);
$this->assertEquals([ $this->assertEquals([
'comments' => [ 'comments' => [
new Comment("// Line\n", 4, 49, 12), new Comment("// Line\n",
new Comment("// Comments\n", 5, 61, 14), 4, 49, 12, 5, 56, 12),
new Comment("// Comments\n",
5, 61, 14, 6, 72, 14),
], ],
'startLine' => 6, 'startLine' => 6,
'endLine' => 6, 'endLine' => 6,