1
0
Fork 0

feat(completion): completion for relative names

pull/602/head
Declspeck 2018-02-16 00:12:08 +02:00
parent 0bc5b81561
commit 75e07b86a0
No known key found for this signature in database
GPG Key ID: F0417663122A2189
3 changed files with 74 additions and 4 deletions

View File

@ -0,0 +1,9 @@
<?php
namespace TestNamespace\InnerNamespace;
use TestNamespace\TestClass as InnerClass;
// Both of these should complete to namespace\InnerClass, for \TestNamespace\InnerNamespace\InnerClass
namespace\;
namespace\InnerCl;

View File

@ -277,6 +277,7 @@ class CompletionProvider
} elseif ( } elseif (
ParserHelpers\isConstantFetch($node) ParserHelpers\isConstantFetch($node)
|| $node instanceof Node\RelativeSpecifier
// Creation gets set in case of an instantiation (`new` expression) // Creation gets set in case of an instantiation (`new` expression)
|| ($creation = $node->parent) instanceof Node\Expression\ObjectCreationExpression || ($creation = $node->parent) instanceof Node\Expression\ObjectCreationExpression
|| (($creation = $node) instanceof Node\Expression\ObjectCreationExpression) || (($creation = $node) instanceof Node\Expression\ObjectCreationExpression)
@ -288,13 +289,19 @@ class CompletionProvider
// MY_CONS| // MY_CONS|
// MyCla| // MyCla|
// \MyCla| // \MyCla|
// namespace\| // This is the special case when Node\RelativeSpecifier is matched.
// namespace\MyC|
// The name Node under the cursor // The name Node under the cursor
$nameNode = isset($creation) ? $creation->classTypeDesignator : $node; $nameNode = isset($creation) ? $creation->classTypeDesignator : $node;
if ($nameNode instanceof Node\QualifiedName) { if ($nameNode instanceof Node\QualifiedName) {
/** @var string The typed name. */ /** @var string The typed name. If relative, without the namespace\. */
$prefix = (string)PhpParser\ResolvedName::buildName($nameNode->nameParts, $nameNode->getFileContents()); $prefix = (string)PhpParser\ResolvedName::buildName($nameNode->nameParts, $nameNode->getFileContents());
} else if ($nameNode instanceof Node\RelativeSpecifier) {
// The prefix is supposed to not have the leading namespace\ specifier. The token at point is
// "namespace\", so prefix should be empty.
$prefix = '';
} else { } else {
$prefix = $nameNode->getText($node->getFileContents()); $prefix = $nameNode->getText($node->getFileContents());
} }
@ -305,13 +312,16 @@ class CompletionProvider
/** @var bool Whether the prefix is qualified (contains at least one backslash) */ /** @var bool Whether the prefix is qualified (contains at least one backslash) */
$isFullyQualified = false; $isFullyQualified = false;
/** @var bool|null Whether the prefix is qualified (contains at least one backslash) */
/** @var bool Whether the prefix is qualified (contains at least one backslash) */
$isQualified = false; $isQualified = false;
/** @var bool Whether the prefix starts with namespace\ */
$isRelative = false;
if ($nameNode instanceof Node\QualifiedName) { if ($nameNode instanceof Node\QualifiedName) {
$isFullyQualified = $nameNode->isFullyQualifiedName(); $isFullyQualified = $nameNode->isFullyQualifiedName();
$isQualified = $nameNode->isQualifiedName(); $isQualified = $nameNode->isQualifiedName();
$isRelative = $nameNode->isRelativeName();
} else if ($nameNode instanceof Node\RelativeSpecifier) {
$isRelative = true;
} }
/** @var bool Whether we are in a new expression */ /** @var bool Whether we are in a new expression */
@ -324,6 +334,9 @@ class CompletionProvider
// \Prefix\Goes\Here| - Only return completions from the root namespace. // \Prefix\Goes\Here| - Only return completions from the root namespace.
/** @var $items \Generator|CompletionItem[] Generator yielding CompletionItems indexed by their FQN */ /** @var $items \Generator|CompletionItem[] Generator yielding CompletionItems indexed by their FQN */
$items = $this->getCompletionsForFqnPrefix($prefix, $isCreation, false); $items = $this->getCompletionsForFqnPrefix($prefix, $isCreation, false);
} else if ($isRelative) {
// namespace\Something| - Only return completions from the current namespace.
$items = $this->getCompletionsForFqnPrefix(nameConcat($currentNamespace, $prefix), $isCreation, false);
} else if ($isQualified) { } else if ($isQualified) {
// Prefix\Goes\Here| // Prefix\Goes\Here|
$items = $this->getPartiallyQualifiedCompletions( $items = $this->getPartiallyQualifiedCompletions(

View File

@ -325,6 +325,54 @@ class CompletionTest extends TestCase
); );
} }
/**
* Tests completion at `namespace\|`
*/
public function testRelativeNoPrefix()
{
$completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/relative.php');
$this->loader->open($completionUri, file_get_contents($completionUri));
$items = $this->textDocument->completion(
new TextDocumentIdentifier($completionUri),
new Position(7, 10)
)->wait();
$this->assertEquals(
new CompletionList([
new CompletionItem(
'InnerClass',
CompletionItemKind::CLASS_,
'TestNamespace\InnerNamespace',
''
),
], true),
$items
);
}
/**
* Tests completion at `namespace\TestCla|`
*/
public function testRelativeWithPrefix()
{
$completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/relative.php');
$this->loader->open($completionUri, file_get_contents($completionUri));
$items = $this->textDocument->completion(
new TextDocumentIdentifier($completionUri),
new Position(8, 17)
)->wait();
$this->assertEquals(
new CompletionList([
new CompletionItem(
'InnerClass',
CompletionItemKind::CLASS_,
'TestNamespace\InnerNamespace',
''
),
], true),
$items
);
}
/** /**
* Tests completion at `TestClass::$st|` * Tests completion at `TestClass::$st|`
*/ */