From 5f085a3d8ab3191262ba1619670ec3d4a6e24af7 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Thu, 24 Nov 2016 23:36:45 +0100 Subject: [PATCH] Support completion for namespaces --- fixtures/completion/namespace.php | 5 +++++ src/CompletionProvider.php | 8 +++++-- src/Definition.php | 1 + src/DefinitionResolver.php | 4 ++++ tests/LanguageServerTest.php | 4 ++-- tests/NodeVisitor/DefinitionCollectorTest.php | 4 +++- tests/Server/ServerTestCase.php | 14 ++++++------- tests/Server/TextDocument/CompletionTest.php | 21 +++++++++++++++++++ .../TextDocument/DocumentSymbolTest.php | 1 + tests/Server/Workspace/SymbolTest.php | 18 ++++++++++++++-- 10 files changed, 66 insertions(+), 14 deletions(-) create mode 100644 fixtures/completion/namespace.php diff --git a/fixtures/completion/namespace.php b/fixtures/completion/namespace.php new file mode 100644 index 0000000..25742e0 --- /dev/null +++ b/fixtures/completion/namespace.php @@ -0,0 +1,5 @@ +canBeInstantiated = $node instanceof Node\Stmt\Class_; $def->isGlobal = ( $node instanceof Node\Stmt\ClassLike + || $node instanceof Node\Stmt\Namespace_ || $node instanceof Node\Stmt\Function_ || $node->getAttribute('parentNode') instanceof Node\Stmt\Const_ ); @@ -189,6 +190,7 @@ class DefinitionResolver if ( $node instanceof Node\Name && ( $parent instanceof Node\Stmt\ClassLike + || $parent instanceof Node\Namespace_ || $parent instanceof Node\Param || $parent instanceof Node\FunctionLike || $parent instanceof Node\Expr\StaticCall @@ -775,6 +777,8 @@ class DefinitionResolver if ($node instanceof Node\Stmt\ClassLike && isset($node->name)) { // Class, interface or trait declaration return (string)$node->namespacedName; + } else if ($node instanceof Node\Stmt\Namespace_) { + return (string)$node->name; } else if ($node instanceof Node\Stmt\Function_) { // Function: use functionName() as the name return (string)$node->namespacedName . '()'; diff --git a/tests/LanguageServerTest.php b/tests/LanguageServerTest.php index 8efac8d..4a35fe6 100644 --- a/tests/LanguageServerTest.php +++ b/tests/LanguageServerTest.php @@ -64,7 +64,7 @@ class LanguageServerTest extends TestCase if ($msg->body->method === 'window/logMessage' && $promise->state === Promise::PENDING) { if ($msg->body->params->type === MessageType::ERROR) { $promise->reject(new Exception($msg->body->params->message)); - } else if (strpos($msg->body->params->message, 'All 24 PHP files parsed') !== false) { + } else if (strpos($msg->body->params->message, 'All 25 PHP files parsed') !== false) { $promise->fulfill(); } } @@ -109,7 +109,7 @@ class LanguageServerTest extends TestCase if ($promise->state === Promise::PENDING) { $promise->reject(new Exception($msg->body->params->message)); } - } else if (strpos($msg->body->params->message, 'All 24 PHP files parsed') !== false) { + } else if (strpos($msg->body->params->message, 'All 25 PHP files parsed') !== false) { // Indexing finished $promise->fulfill(); } diff --git a/tests/NodeVisitor/DefinitionCollectorTest.php b/tests/NodeVisitor/DefinitionCollectorTest.php index 6768c94..74e0d5c 100644 --- a/tests/NodeVisitor/DefinitionCollectorTest.php +++ b/tests/NodeVisitor/DefinitionCollectorTest.php @@ -30,6 +30,7 @@ class DefinitionCollectorTest extends TestCase $traverser->traverse($stmts); $defNodes = $definitionCollector->nodes; $this->assertEquals([ + 'TestNamespace', 'TestNamespace\\TEST_CONST', 'TestNamespace\\TestClass', 'TestNamespace\\TestClass::TEST_CLASS_CONST', @@ -68,7 +69,8 @@ class DefinitionCollectorTest extends TestCase $stmts = $parser->parse(file_get_contents($uri)); $traverser->traverse($stmts); $defNodes = $definitionCollector->nodes; - $this->assertEquals(['TestNamespace\\whatever()'], array_keys($defNodes)); + $this->assertEquals(['TestNamespace', 'TestNamespace\\whatever()'], array_keys($defNodes)); + $this->assertInstanceOf(Node\Stmt\Namespace_::class, $defNodes['TestNamespace']); $this->assertInstanceOf(Node\Stmt\Function_::class, $defNodes['TestNamespace\\whatever()']); } } diff --git a/tests/Server/ServerTestCase.php b/tests/Server/ServerTestCase.php index 1a608ca..23d1763 100644 --- a/tests/Server/ServerTestCase.php +++ b/tests/Server/ServerTestCase.php @@ -54,13 +54,11 @@ abstract class ServerTestCase extends TestCase $referencesUri = pathToUri(realpath(__DIR__ . '/../../fixtures/references.php')); $useUri = pathToUri(realpath(__DIR__ . '/../../fixtures/use.php')); - Promise\all([ - $this->project->loadDocument($symbolsUri), - $this->project->loadDocument($referencesUri), - $this->project->loadDocument($globalSymbolsUri), - $this->project->loadDocument($globalReferencesUri), - $this->project->loadDocument($useUri) - ])->wait(); + $this->project->loadDocument($symbolsUri)->wait(); + $this->project->loadDocument($referencesUri)->wait(); + $this->project->loadDocument($globalSymbolsUri)->wait(); + $this->project->loadDocument($globalReferencesUri)->wait(); + $this->project->loadDocument($useUri)->wait(); // @codingStandardsIgnoreStart $this->definitionLocations = [ @@ -79,6 +77,8 @@ abstract class ServerTestCase extends TestCase 'whatever()' => new Location($globalReferencesUri, new Range(new Position(21, 0), new Position(23, 1))), // Namespaced + 'TestNamespace' => new Location($symbolsUri, new Range(new Position( 2, 0), new Position( 2, 24))), + 'SecondTestNamespace' => new Location($useUri, new Range(new Position( 2, 0), new Position( 2, 30))), 'TestNamespace\\TEST_CONST' => new Location($symbolsUri, new Range(new Position( 9, 6), new Position( 9, 22))), 'TestNamespace\\TestClass' => new Location($symbolsUri, new Range(new Position(20, 0), new Position(61, 1))), 'TestNamespace\\TestTrait' => new Location($symbolsUri, new Range(new Position(63, 0), new Position(66, 1))), diff --git a/tests/Server/TextDocument/CompletionTest.php b/tests/Server/TextDocument/CompletionTest.php index f2e8028..c76c7fd 100644 --- a/tests/Server/TextDocument/CompletionTest.php +++ b/tests/Server/TextDocument/CompletionTest.php @@ -358,4 +358,25 @@ class CompletionTest extends TestCase ) ], $items); } + + public function testNamespace() + { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/namespace.php'); + $this->project->openDocument($completionUri, file_get_contents($completionUri)); + $items = $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(4, 6) + )->wait(); + $this->assertEquals([ + new CompletionItem( + 'SomeNamespace', + CompletionItemKind::MODULE, + null, + null, + null, + null, + 'SomeNamespace' + ) + ], $items); + } } diff --git a/tests/Server/TextDocument/DocumentSymbolTest.php b/tests/Server/TextDocument/DocumentSymbolTest.php index 4f09e20..b9c937e 100644 --- a/tests/Server/TextDocument/DocumentSymbolTest.php +++ b/tests/Server/TextDocument/DocumentSymbolTest.php @@ -18,6 +18,7 @@ class DocumentSymbolTest extends ServerTestCase $result = $this->textDocument->documentSymbol(new TextDocumentIdentifier($uri))->wait(); // @codingStandardsIgnoreStart $this->assertEquals([ + new SymbolInformation('TestNamespace', SymbolKind::NAMESPACE, $this->getDefinitionLocation('TestNamespace'), ''), new SymbolInformation('TEST_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestNamespace\\TEST_CONST'), 'TestNamespace'), new SymbolInformation('TestClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\TestClass'), 'TestNamespace'), new SymbolInformation('TEST_CLASS_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestNamespace\\TestClass::TEST_CLASS_CONST'), 'TestNamespace\\TestClass'), diff --git a/tests/Server/Workspace/SymbolTest.php b/tests/Server/Workspace/SymbolTest.php index 7086942..33b4cf1 100644 --- a/tests/Server/Workspace/SymbolTest.php +++ b/tests/Server/Workspace/SymbolTest.php @@ -6,7 +6,17 @@ namespace LanguageServer\Tests\Server\Workspace; use LanguageServer\Tests\MockProtocolStream; use LanguageServer\Tests\Server\ServerTestCase; use LanguageServer\{Server, Client, LanguageClient, Project, PhpDocument}; -use LanguageServer\Protocol\{TextDocumentItem, TextDocumentIdentifier, SymbolInformation, SymbolKind, DiagnosticSeverity, FormattingOptions}; +use LanguageServer\Protocol\{ + TextDocumentItem, + TextDocumentIdentifier, + SymbolInformation, + SymbolKind, + DiagnosticSeverity, + FormattingOptions, + Location, + Range, + Position +}; use AdvancedJsonRpc\{Request as RequestBody, Response as ResponseBody}; use function LanguageServer\pathToUri; @@ -16,8 +26,10 @@ class SymbolTest extends ServerTestCase { // Request symbols $result = $this->workspace->symbol(''); + $referencesUri = pathToUri(realpath(__DIR__ . '/../../../fixtures/references.php')); // @codingStandardsIgnoreStart $this->assertEquals([ + new SymbolInformation('TestNamespace', SymbolKind::NAMESPACE, new Location($referencesUri, new Range(new Position(2, 0), new Position(2, 24))), ''), // Namespaced new SymbolInformation('TEST_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestNamespace\\TEST_CONST'), 'TestNamespace'), new SymbolInformation('TestClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\TestClass'), 'TestNamespace'), @@ -41,7 +53,9 @@ class SymbolTest extends ServerTestCase new SymbolInformation('TestTrait', SymbolKind::CLASS_, $this->getDefinitionLocation('TestTrait'), ''), new SymbolInformation('TestInterface', SymbolKind::INTERFACE, $this->getDefinitionLocation('TestInterface'), ''), new SymbolInformation('test_function', SymbolKind::FUNCTION, $this->getDefinitionLocation('test_function()'), ''), - new SymbolInformation('whatever', SymbolKind::FUNCTION, $this->getDefinitionLocation('whatever()'), '') + new SymbolInformation('whatever', SymbolKind::FUNCTION, $this->getDefinitionLocation('whatever()'), ''), + + new SymbolInformation('SecondTestNamespace', SymbolKind::NAMESPACE, $this->getDefinitionLocation('SecondTestNamespace'), '') ], $result); // @codingStandardsIgnoreEnd }