Properly filter completion on empty property
parent
51de0b5dfc
commit
d66cc763bc
|
@ -1,4 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
$obj = new TestClass;
|
$obj = new TestClass;
|
||||||
$obj->t
|
$obj->
|
||||||
|
|
|
@ -56,12 +56,36 @@ class CompletionProvider
|
||||||
|| $node instanceof Node\Expr\StaticPropertyFetch
|
|| $node instanceof Node\Expr\StaticPropertyFetch
|
||||||
|| $node instanceof Node\Expr\ClassConstFetch
|
|| $node instanceof Node\Expr\ClassConstFetch
|
||||||
) {
|
) {
|
||||||
/** The FQN to be completed */
|
$nodeToResolve = $node;
|
||||||
$prefix = $this->definitionResolver->resolveReferenceNodeToFqn($node) ?? '';
|
if (!is_string($node->name)) {
|
||||||
$prefixLen = strlen($prefix);
|
// If the name is an Error node, just filter by the class
|
||||||
|
if ($node instanceof Node\Expr\MethodCall || $node instanceof Node\Expr\PropertyFetch) {
|
||||||
|
$nodeToResolve = $node->var;
|
||||||
|
} else {
|
||||||
|
$nodeToResolve = $node->class;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$prefixes = DefinitionResolver::getFqnsFromType(
|
||||||
|
$this->definitionResolver->resolveExpressionNodeToType($nodeToResolve)
|
||||||
|
);
|
||||||
|
if (!is_string($node->name)) {
|
||||||
|
// If we are just filtering by the class, add the appropiate operator to the prefix
|
||||||
|
// to filter the type of symbol
|
||||||
|
foreach ($prefixes as &$prefix) {
|
||||||
|
if ($node instanceof Node\Expr\MethodCall || $node instanceof Node\Expr\PropertyFetch) {
|
||||||
|
$prefix .= '->';
|
||||||
|
} else if ($node instanceof Node\Expr\StaticCall || $node instanceof Node\Expr\ClassConstFetch) {
|
||||||
|
$prefix .= '::';
|
||||||
|
} else if ($node instanceof Node\Expr\StaticPropertyFetch) {
|
||||||
|
$prefix .= '::$';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
foreach ($this->project->getDefinitions() as $fqn => $def) {
|
foreach ($this->project->getDefinitions() as $fqn => $def) {
|
||||||
if (substr($fqn, 0, $prefixLen) === $prefix && !$def->isGlobal) {
|
foreach ($prefixes as $prefix) {
|
||||||
$items[] = CompletionItem::fromDefinition($def);
|
if (substr($fqn, 0, strlen($prefix)) === $prefix && !$def->isGlobal) {
|
||||||
|
$items[] = CompletionItem::fromDefinition($def);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
|
|
|
@ -146,6 +146,31 @@ class DefinitionResolver
|
||||||
return $this->project->getDefinition($fqn, $globalFallback);
|
return $this->project->getDefinition($fqn, $globalFallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all possible FQNs in a type
|
||||||
|
*
|
||||||
|
* @param Type $type
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public static function getFqnsFromType(Type $type): array
|
||||||
|
{
|
||||||
|
$fqns = [];
|
||||||
|
if ($type instanceof Types\Object_) {
|
||||||
|
$fqsen = $type->getFqsen();
|
||||||
|
if ($fqsen !== null) {
|
||||||
|
$fqns[] = substr((string)$fqsen, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($type instanceof Types\Compound) {
|
||||||
|
for ($i = 0; $t = $type->get($i); $i++) {
|
||||||
|
foreach (self::getFqnsFromType($type) as $fqn) {
|
||||||
|
$fqns[] = $fqn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $fqns;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given any node, returns the FQN of the symbol that is referenced
|
* Given any node, returns the FQN of the symbol that is referenced
|
||||||
* Returns null if the FQN could not be resolved or the reference node references a variable
|
* Returns null if the FQN could not be resolved or the reference node references a variable
|
||||||
|
|
|
@ -30,13 +30,37 @@ class CompletionTest extends TestCase
|
||||||
$this->textDocument = new Server\TextDocument($this->project, $client);
|
$this->textDocument = new Server\TextDocument($this->project, $client);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testForPropertiesAndMethods()
|
public function testPropertyAndMethodWithPrefix()
|
||||||
|
{
|
||||||
|
$completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/property_with_prefix.php');
|
||||||
|
$this->project->openDocument($completionUri, file_get_contents($completionUri));
|
||||||
|
$items = $this->textDocument->completion(
|
||||||
|
new TextDocumentIdentifier($completionUri),
|
||||||
|
new Position(3, 7)
|
||||||
|
)->wait();
|
||||||
|
$this->assertEquals([
|
||||||
|
new CompletionItem(
|
||||||
|
'testProperty',
|
||||||
|
CompletionItemKind::PROPERTY,
|
||||||
|
'\TestClass', // Type of the property
|
||||||
|
'Reprehenderit magna velit mollit ipsum do.'
|
||||||
|
),
|
||||||
|
new CompletionItem(
|
||||||
|
'testMethod',
|
||||||
|
CompletionItemKind::METHOD,
|
||||||
|
'\TestClass', // Return type of the method
|
||||||
|
'Non culpa nostrud mollit esse sunt laboris in irure ullamco cupidatat amet.'
|
||||||
|
)
|
||||||
|
], $items);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPropertyAndMethodWithoutPrefix()
|
||||||
{
|
{
|
||||||
$completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/property.php');
|
$completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/property.php');
|
||||||
$this->project->openDocument($completionUri, file_get_contents($completionUri));
|
$this->project->openDocument($completionUri, file_get_contents($completionUri));
|
||||||
$items = $this->textDocument->completion(
|
$items = $this->textDocument->completion(
|
||||||
new TextDocumentIdentifier($completionUri),
|
new TextDocumentIdentifier($completionUri),
|
||||||
new Position(3, 7)
|
new Position(3, 6)
|
||||||
)->wait();
|
)->wait();
|
||||||
$this->assertEquals([
|
$this->assertEquals([
|
||||||
new CompletionItem(
|
new CompletionItem(
|
||||||
|
|
Loading…
Reference in New Issue