Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Seems to work
  • Loading branch information
dantleech committed Dec 7, 2025
commit ce5774da36ba4ad76f244f4025496dd9733324b8
18 changes: 17 additions & 1 deletion lib/TolerantAstDiff/AstDiff.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ final class AstDiff

private SourceFileNode $fileSource2;

private array $visited = [];

public function merge(Node $node1, Node $node2): void
{
$this->visited = [];
$this->fileSource1 = $node1->getRoot();
$this->fileSource2 = $node2->getRoot();

Expand All @@ -28,6 +31,18 @@ public function merge(Node $node1, Node $node2): void

private function doMerge(Node $node1, Node $node2): void
{
if ($node1->getText() === $node2->getText()) {
return;
}

// check for circular references
// TODO: this was added for debugging can probably be removed
if (isset($this->visited[spl_object_id($node1)])) {
throw new \RuntimeException('Circular reference');
}

$this->visited[spl_object_id($node1)] = true;

if ($node1::class !== $node2::class) {
throw new RuntimeException(sprintf(
'Can only compare nodes of the same type, got: %s vs %s',
Expand Down Expand Up @@ -257,6 +272,7 @@ private function appendChildren(Node $parent, string $childName, array $newNodes
}

private function applyEdit(TextEdit $edit): void

{
$source = $this->fileSource1;
$source->fileContents = TextEdits::one($edit)->apply($source->getFileContents());
Expand All @@ -267,7 +283,7 @@ private static function reindex(Node $node): void
{
$offset = 0;
foreach ($node->getDescendantTokens() as $token) {
$leading = $token->fullStart - $token->start;
$leading = $token->start - $token->fullStart;

$token->fullStart = $offset;
$token->start = $offset + $leading;
Expand Down
24 changes: 24 additions & 0 deletions lib/TolerantAstDiff/Tests/Benchmark/AstDiffBench.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace Phpactor\TolerantAstDiff\Tests\Benchmark;

use Phpactor\TolerantAstDiff\AstDiff;
use Phpactor\WorseReflection\Bridge\TolerantParser\Parser\MergingParser;

final class AstDiffBench
{
public function benchMerge(): void
{
$largeFile = __DIR__ . '/../../AstDiff.php';
$mergingParser = new MergingParser(new AstDiff());
$contents = file_get_contents($largeFile);

$ast1 = $mergingParser->parseSourceFile($contents, __FILE__);

$contents = (string)substr($contents, 0, 150);
$contents .= "\n";
$contents .= "\n";

$ast2 = $mergingParser->parseSourceFile($contents, __FILE__);
}
}
15 changes: 5 additions & 10 deletions lib/WorseReflection/Bridge/TolerantParser/Parser/MergingParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,27 +24,22 @@ public function __construct(

public function parseSourceFile(string $source, ?string $uri = null): SourceFileNode
{
if ($uri === null) {
if (!$uri) {
return parent::parseSourceFile($source);
}



if (!isset($this->documents[$uri])) {

@dantleech dantleech Dec 7, 2025 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should probably, for now, use a Ttl caching mechanism, but should use the #2882

$node = parent::parseSourceFile($source, $uri);
$this->documents[$uri] = $node;
return $node;
}

$node1 = $this->documents[$uri];
$node2 = parent::parseSourceFile($source, $uri);

$start = microtime(true);
$node2 = parent::parseSourceFile($source, $uri);
$this->merger->merge($node1, $node2);

if ($uri === 'file://'.__FILE__) {
$this->merger->merge($node1, $node2);
$this->merger->merge($node1, $node2);
}

return $node2;
return $node1;
}
}