Skip to content

Support for Variable and MethodCall with chain for autocompletion #17

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
14 changes: 10 additions & 4 deletions app/Contexts/AbstractContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ abstract class AbstractContext
{
public array $children = [];

/**
* Whether this context can be found as last result
* in findAutocompleting method
*/
public bool $findable = false;

public bool $autocompleting = false;

protected array $freshObject;
Expand Down Expand Up @@ -48,20 +54,20 @@ public function flip()
public function findAutocompleting(?AbstractContext $context = null)
{
$context = $context ?? $this;
$result = $this->seachForAutocompleting($context, true);
$result = $this->searchForAutocompleting($context, true);
$lastResult = null;

while ($result !== null) {
$lastResult = $result;
$result = $this->seachForAutocompleting($result);
$result = $this->searchForAutocompleting($result);
}

return $lastResult;
}

protected function seachForAutocompleting(AbstractContext $context, $checkCurrent = false)
protected function searchForAutocompleting(AbstractContext $context, $checkCurrent = false)
{
if ($checkCurrent && $context->autocompleting && ($context instanceof MethodCall || $context instanceof ObjectValue)) {
if ($checkCurrent && $context->autocompleting && $context->findable) {
return $context;
}

Expand Down
2 changes: 2 additions & 0 deletions app/Contexts/MethodCall.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

class MethodCall extends AbstractContext
{
public bool $findable = true;

public ?string $methodName = null;

public ?string $className = null;
Expand Down
2 changes: 2 additions & 0 deletions app/Contexts/ObjectValue.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

class ObjectValue extends AbstractContext
{
public bool $findable = true;

public ?string $className = null;

public Arguments $arguments;
Expand Down
24 changes: 24 additions & 0 deletions app/Contexts/Variable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace App\Contexts;

class Variable extends AbstractContext
{
public bool $findable = true;

public ?string $name = null;

protected bool $hasChildren = false;

public function type(): string
{
return 'variable';
}

public function castToArray(): array
{
return [
'name' => $this->name,
];
}
}
13 changes: 12 additions & 1 deletion app/Parser/Walker.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,20 @@ protected function documentSkipsClosingQuote()
return false;
}

protected function documentSkipsObjectOperator(): bool
{
if (count($this->sourceFile->statementList) === 1 && $this->sourceFile->statementList[0] instanceof InlineHtml) {
$lastChars = substr($this->sourceFile->getFullText(), -2);

return $lastChars === '->';
}

return false;
}

public function walk()
{
if (!$this->documentSkipsClosingQuote()) {
if (!$this->documentSkipsClosingQuote() && !$this->documentSkipsObjectOperator()) {
return new Base;
}

Expand Down
29 changes: 24 additions & 5 deletions app/Parsers/MemberAccessExpressionParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,23 @@ class MemberAccessExpressionParser extends AbstractParser
*/
protected AbstractContext $context;

/**
* Check if the node has a object operator and
* is a last element in the string
*/
private function hasObjectOperator(MemberAccessExpression $node): bool
{
$name = $node->memberName->getFullText($node->getRoot()->getFullText());

return preg_match('/->' . $name . '->;$/s', $node->getFileContents());
}

public function parse(MemberAccessExpression $node)
{
if ($this->hasObjectOperator($node)) {
$this->context->autocompleting = true;
}

$this->context->methodName = $node->memberName->getFullText($node->getRoot()->getFullText());

foreach ($node->getDescendantNodes() as $child) {
Expand All @@ -30,7 +45,9 @@ public function parse(MemberAccessExpression $node)

if ($child instanceof Variable) {
if ($child->getName() === 'this') {
if ($child->getParent()->getParent() instanceof CallExpression) {
$parent = $child->getParent();

if ($parent?->getParent() instanceof CallExpression) {
// They are calling a method on the current class
$result = $this->context->nearestClassDefinition();

Expand All @@ -41,12 +58,14 @@ public function parse(MemberAccessExpression $node)
continue;
}

$propName = $child->getParent()->memberName->getFullText($node->getRoot()->getFullText());
if ($parent instanceof MemberAccessExpression) {
$propName = $parent->memberName->getFullText($node->getRoot()->getFullText());

$result = $this->context->searchForProperty($propName);
$result = $this->context->searchForProperty($propName);

if ($result) {
$this->context->className = $result['types'][0] ?? null;
if ($result) {
$this->context->className = $result['types'][0] ?? null;
}
}

continue;
Expand Down
58 changes: 58 additions & 0 deletions app/Parsers/VariableParser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

namespace App\Parsers;

use App\Contexts\AbstractContext;
use App\Contexts\Variable as VariableContext;
use App\Parser\Settings;
use Microsoft\PhpParser\Node\Expression\Variable;
use Microsoft\PhpParser\PositionUtilities;

class VariableParser extends AbstractParser
{
/**
* @var VariableContext
*/
protected AbstractContext $context;

/**
* Check if the node has a object operator and
* is a last element in the string
*/
private function hasObjectOperator(Variable $node): bool
{
$name = $node->getName();

return preg_match('/\$' . $name . '->;$/s', $node->getFileContents());
}

public function parse(Variable $node)
{
if ($this->hasObjectOperator($node)) {
$this->context->autocompleting = true;
}

$this->context->name = $node->getName();

if (Settings::$capturePosition) {
$range = PositionUtilities::getRangeFromPosition(
$node->getStartPosition(),
mb_strlen($node->getText()),
$node->getRoot()->getFullText(),
);

if (Settings::$calculatePosition !== null) {
$range = Settings::adjustPosition($range);
}

$this->context->setPosition($range);
}

return $this->context;
}

public function initNewContext(): ?AbstractContext
{
return new VariableContext;
}
}