-
-
Notifications
You must be signed in to change notification settings - Fork 117
/
ClassLikeUpdater.php
127 lines (108 loc) · 4.44 KB
/
ClassLikeUpdater.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
<?php
namespace Phpactor\CodeBuilder\Adapter\TolerantParser\Updater;
use Microsoft\PhpParser\Node;
use Microsoft\PhpParser\Node\ClassConstDeclaration;
use Microsoft\PhpParser\Node\Expression\AssignmentExpression;
use Microsoft\PhpParser\Node\Expression\ObjectCreationExpression;
use Microsoft\PhpParser\Node\Expression\Variable;
use Microsoft\PhpParser\Node\MethodDeclaration;
use Microsoft\PhpParser\Node\PropertyDeclaration;
use Microsoft\PhpParser\Node\Statement\ClassDeclaration;
use Microsoft\PhpParser\Node\Statement\EnumDeclaration;
use Microsoft\PhpParser\Node\Statement\InterfaceDeclaration;
use Microsoft\PhpParser\Node\Statement\TraitDeclaration;
use Microsoft\PhpParser\Token;
use Phpactor\CodeBuilder\Adapter\TolerantParser\Edits;
use Phpactor\CodeBuilder\Domain\Prototype\ClassLikePrototype;
use Phpactor\CodeBuilder\Domain\Prototype\Type;
use Phpactor\CodeBuilder\Domain\Renderer;
use InvalidArgumentException;
use Phpactor\TextDocument\TextEdit;
abstract class ClassLikeUpdater
{
protected ClassMethodUpdater $methodUpdater;
public function __construct(protected Renderer $renderer)
{
$this->methodUpdater = new ClassMethodUpdater($renderer);
}
protected function resolvePropertyName(Node|Token $property): ?string
{
if ($property instanceof Variable) {
return $property->getName();
}
if ($property instanceof AssignmentExpression) {
return $this->resolvePropertyName($property->leftOperand);
}
throw new InvalidArgumentException(sprintf(
'Do not know how to resolve property element of type "%s"',
get_class($property)
));
}
/** @return array<Node> */
abstract protected function memberDeclarations(Node $node): array;
protected function updateProperties(Edits $edits, ClassLikePrototype $classPrototype, Node $classMembers): void
{
if (count($classPrototype->properties()) === 0) {
return;
}
$memberDeclarations = $this->memberDeclarations($classMembers);
$lastProperty = $this->getInsertPlace($classMembers, $memberDeclarations);
$nextMember = null;
$existingPropertyNames = [];
foreach ($memberDeclarations as $memberNode) {
if (null === $nextMember) {
$nextMember = $memberNode;
}
if ($memberNode instanceof PropertyDeclaration) {
foreach ($memberNode->propertyElements->getElements() as $property) {
$existingPropertyNames[] = $this->resolvePropertyName($property);
}
$lastProperty = $memberNode;
$nextMember = next($memberDeclarations) ?: $nextMember;
prev($memberDeclarations);
}
}
foreach ($classPrototype->properties()->notIn($existingPropertyNames) as $property) {
// if property type exists then the last property has a docblock - add a line break
if ($lastProperty instanceof PropertyDeclaration && $property->type() != Type::none()) {
$edits->after($lastProperty, "\n");
}
$edits->after(
$lastProperty,
"\n" . $edits->indent($this->renderer->render($property), 1)
);
if ($classPrototype->properties()->isLast($property) && $nextMember instanceof MethodDeclaration) {
$edits->after($lastProperty, "\n");
}
}
}
/**
* @param Node[] $memberDeclarations
*/
protected function getInsertPlace(Node $classNode, array $memberDeclarations): Token
{
$insert = $classNode->openBrace;
foreach ($memberDeclarations as $member) {
if ($member instanceof ClassConstDeclaration) {
$insert = $member->semicolon;
} else {
break;
}
}
return $insert;
}
protected function updateDocblock(
Edits $edits,
ClassLikePrototype $classPrototype,
ClassDeclaration|TraitDeclaration|EnumDeclaration|InterfaceDeclaration|ObjectCreationExpression $classLikeDeclaration,
): void {
if (!$classPrototype->docblock()->notNone()) {
return;
}
$edits->add(TextEdit::create(
$classLikeDeclaration->getFullStartPosition(),
strlen($classLikeDeclaration->getLeadingCommentAndWhitespaceText()),
$classPrototype->docblock()->__toString()
));
}
}