From 75e07b86a024e458912e380fcdc25930b83db813 Mon Sep 17 00:00:00 2001 From: Declspeck Date: Fri, 16 Feb 2018 00:12:08 +0200 Subject: [PATCH] feat(completion): completion for relative names --- fixtures/completion/relative.php | 9 ++++ src/CompletionProvider.php | 21 +++++++-- tests/Server/TextDocument/CompletionTest.php | 48 ++++++++++++++++++++ 3 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 fixtures/completion/relative.php diff --git a/fixtures/completion/relative.php b/fixtures/completion/relative.php new file mode 100644 index 0000000..2bac9c6 --- /dev/null +++ b/fixtures/completion/relative.php @@ -0,0 +1,9 @@ +parent) instanceof Node\Expression\ObjectCreationExpression || (($creation = $node) instanceof Node\Expression\ObjectCreationExpression) @@ -288,13 +289,19 @@ class CompletionProvider // MY_CONS| // MyCla| // \MyCla| + // namespace\| // This is the special case when Node\RelativeSpecifier is matched. + // namespace\MyC| // The name Node under the cursor $nameNode = isset($creation) ? $creation->classTypeDesignator : $node; 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()); + } 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 { $prefix = $nameNode->getText($node->getFileContents()); } @@ -305,13 +312,16 @@ class CompletionProvider /** @var bool Whether the prefix is qualified (contains at least one backslash) */ $isFullyQualified = false; - - /** @var bool Whether the prefix is qualified (contains at least one backslash) */ + /** @var bool|null Whether the prefix is qualified (contains at least one backslash) */ $isQualified = false; - + /** @var bool Whether the prefix starts with namespace\ */ + $isRelative = false; if ($nameNode instanceof Node\QualifiedName) { $isFullyQualified = $nameNode->isFullyQualifiedName(); $isQualified = $nameNode->isQualifiedName(); + $isRelative = $nameNode->isRelativeName(); + } else if ($nameNode instanceof Node\RelativeSpecifier) { + $isRelative = true; } /** @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. /** @var $items \Generator|CompletionItem[] Generator yielding CompletionItems indexed by their FQN */ $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) { // Prefix\Goes\Here| $items = $this->getPartiallyQualifiedCompletions( diff --git a/tests/Server/TextDocument/CompletionTest.php b/tests/Server/TextDocument/CompletionTest.php index de33f9f..5e2663e 100644 --- a/tests/Server/TextDocument/CompletionTest.php +++ b/tests/Server/TextDocument/CompletionTest.php @@ -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|` */