1
0
Fork 0

Support completion for namespaces

pull/165/head
Felix Becker 2016-11-24 23:36:45 +01:00
parent 6fb21817e4
commit 5f085a3d8a
10 changed files with 66 additions and 14 deletions

View File

@ -0,0 +1,5 @@
<?php
namespace SomeNamespace {}
SomeNa

View File

@ -217,8 +217,12 @@ class CompletionProvider
&& ( && (
!$prefix !$prefix
|| ( || (
($isFullyQualified && substr($fqn, 0, $prefixLen) === $prefix) ((!$namespace || $isFullyQualified) && substr($fqn, 0, $prefixLen) === $prefix)
|| (!$isFullyQualified && substr($fqn, 0, $namespacedPrefixLen) === $namespacedPrefix) || (
$namespace
&& !$isFullyQualified
&& substr($fqn, 0, $namespacedPrefixLen) === $namespacedPrefix
)
) )
) )
// Only suggest classes for `new` // Only suggest classes for `new`

View File

@ -18,6 +18,7 @@ class Definition
* *
* Examples of FQNs: * Examples of FQNs:
* - testFunction() * - testFunction()
* - TestNamespace
* - TestNamespace\TestClass * - TestNamespace\TestClass
* - TestNamespace\TestClass::TEST_CONSTANT * - TestNamespace\TestClass::TEST_CONSTANT
* - TestNamespace\TestClass::$staticTestProperty * - TestNamespace\TestClass::$staticTestProperty

View File

@ -104,6 +104,7 @@ class DefinitionResolver
$def->canBeInstantiated = $node instanceof Node\Stmt\Class_; $def->canBeInstantiated = $node instanceof Node\Stmt\Class_;
$def->isGlobal = ( $def->isGlobal = (
$node instanceof Node\Stmt\ClassLike $node instanceof Node\Stmt\ClassLike
|| $node instanceof Node\Stmt\Namespace_
|| $node instanceof Node\Stmt\Function_ || $node instanceof Node\Stmt\Function_
|| $node->getAttribute('parentNode') instanceof Node\Stmt\Const_ || $node->getAttribute('parentNode') instanceof Node\Stmt\Const_
); );
@ -189,6 +190,7 @@ class DefinitionResolver
if ( if (
$node instanceof Node\Name && ( $node instanceof Node\Name && (
$parent instanceof Node\Stmt\ClassLike $parent instanceof Node\Stmt\ClassLike
|| $parent instanceof Node\Namespace_
|| $parent instanceof Node\Param || $parent instanceof Node\Param
|| $parent instanceof Node\FunctionLike || $parent instanceof Node\FunctionLike
|| $parent instanceof Node\Expr\StaticCall || $parent instanceof Node\Expr\StaticCall
@ -775,6 +777,8 @@ class DefinitionResolver
if ($node instanceof Node\Stmt\ClassLike && isset($node->name)) { if ($node instanceof Node\Stmt\ClassLike && isset($node->name)) {
// Class, interface or trait declaration // Class, interface or trait declaration
return (string)$node->namespacedName; return (string)$node->namespacedName;
} else if ($node instanceof Node\Stmt\Namespace_) {
return (string)$node->name;
} else if ($node instanceof Node\Stmt\Function_) { } else if ($node instanceof Node\Stmt\Function_) {
// Function: use functionName() as the name // Function: use functionName() as the name
return (string)$node->namespacedName . '()'; return (string)$node->namespacedName . '()';

View File

@ -64,7 +64,7 @@ class LanguageServerTest extends TestCase
if ($msg->body->method === 'window/logMessage' && $promise->state === Promise::PENDING) { if ($msg->body->method === 'window/logMessage' && $promise->state === Promise::PENDING) {
if ($msg->body->params->type === MessageType::ERROR) { if ($msg->body->params->type === MessageType::ERROR) {
$promise->reject(new Exception($msg->body->params->message)); $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(); $promise->fulfill();
} }
} }
@ -109,7 +109,7 @@ class LanguageServerTest extends TestCase
if ($promise->state === Promise::PENDING) { if ($promise->state === Promise::PENDING) {
$promise->reject(new Exception($msg->body->params->message)); $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 // Indexing finished
$promise->fulfill(); $promise->fulfill();
} }

View File

@ -30,6 +30,7 @@ class DefinitionCollectorTest extends TestCase
$traverser->traverse($stmts); $traverser->traverse($stmts);
$defNodes = $definitionCollector->nodes; $defNodes = $definitionCollector->nodes;
$this->assertEquals([ $this->assertEquals([
'TestNamespace',
'TestNamespace\\TEST_CONST', 'TestNamespace\\TEST_CONST',
'TestNamespace\\TestClass', 'TestNamespace\\TestClass',
'TestNamespace\\TestClass::TEST_CLASS_CONST', 'TestNamespace\\TestClass::TEST_CLASS_CONST',
@ -68,7 +69,8 @@ class DefinitionCollectorTest extends TestCase
$stmts = $parser->parse(file_get_contents($uri)); $stmts = $parser->parse(file_get_contents($uri));
$traverser->traverse($stmts); $traverser->traverse($stmts);
$defNodes = $definitionCollector->nodes; $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()']); $this->assertInstanceOf(Node\Stmt\Function_::class, $defNodes['TestNamespace\\whatever()']);
} }
} }

View File

@ -54,13 +54,11 @@ abstract class ServerTestCase extends TestCase
$referencesUri = pathToUri(realpath(__DIR__ . '/../../fixtures/references.php')); $referencesUri = pathToUri(realpath(__DIR__ . '/../../fixtures/references.php'));
$useUri = pathToUri(realpath(__DIR__ . '/../../fixtures/use.php')); $useUri = pathToUri(realpath(__DIR__ . '/../../fixtures/use.php'));
Promise\all([ $this->project->loadDocument($symbolsUri)->wait();
$this->project->loadDocument($symbolsUri), $this->project->loadDocument($referencesUri)->wait();
$this->project->loadDocument($referencesUri), $this->project->loadDocument($globalSymbolsUri)->wait();
$this->project->loadDocument($globalSymbolsUri), $this->project->loadDocument($globalReferencesUri)->wait();
$this->project->loadDocument($globalReferencesUri), $this->project->loadDocument($useUri)->wait();
$this->project->loadDocument($useUri)
])->wait();
// @codingStandardsIgnoreStart // @codingStandardsIgnoreStart
$this->definitionLocations = [ $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))), 'whatever()' => new Location($globalReferencesUri, new Range(new Position(21, 0), new Position(23, 1))),
// Namespaced // 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\\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\\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))), 'TestNamespace\\TestTrait' => new Location($symbolsUri, new Range(new Position(63, 0), new Position(66, 1))),

View File

@ -358,4 +358,25 @@ class CompletionTest extends TestCase
) )
], $items); ], $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);
}
} }

View File

@ -18,6 +18,7 @@ class DocumentSymbolTest extends ServerTestCase
$result = $this->textDocument->documentSymbol(new TextDocumentIdentifier($uri))->wait(); $result = $this->textDocument->documentSymbol(new TextDocumentIdentifier($uri))->wait();
// @codingStandardsIgnoreStart // @codingStandardsIgnoreStart
$this->assertEquals([ $this->assertEquals([
new SymbolInformation('TestNamespace', SymbolKind::NAMESPACE, $this->getDefinitionLocation('TestNamespace'), ''),
new SymbolInformation('TEST_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestNamespace\\TEST_CONST'), '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('TestClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\TestClass'), 'TestNamespace'),
new SymbolInformation('TEST_CLASS_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestNamespace\\TestClass::TEST_CLASS_CONST'), 'TestNamespace\\TestClass'), new SymbolInformation('TEST_CLASS_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestNamespace\\TestClass::TEST_CLASS_CONST'), 'TestNamespace\\TestClass'),

View File

@ -6,7 +6,17 @@ namespace LanguageServer\Tests\Server\Workspace;
use LanguageServer\Tests\MockProtocolStream; use LanguageServer\Tests\MockProtocolStream;
use LanguageServer\Tests\Server\ServerTestCase; use LanguageServer\Tests\Server\ServerTestCase;
use LanguageServer\{Server, Client, LanguageClient, Project, PhpDocument}; 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 AdvancedJsonRpc\{Request as RequestBody, Response as ResponseBody};
use function LanguageServer\pathToUri; use function LanguageServer\pathToUri;
@ -16,8 +26,10 @@ class SymbolTest extends ServerTestCase
{ {
// Request symbols // Request symbols
$result = $this->workspace->symbol(''); $result = $this->workspace->symbol('');
$referencesUri = pathToUri(realpath(__DIR__ . '/../../../fixtures/references.php'));
// @codingStandardsIgnoreStart // @codingStandardsIgnoreStart
$this->assertEquals([ $this->assertEquals([
new SymbolInformation('TestNamespace', SymbolKind::NAMESPACE, new Location($referencesUri, new Range(new Position(2, 0), new Position(2, 24))), ''),
// Namespaced // Namespaced
new SymbolInformation('TEST_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestNamespace\\TEST_CONST'), '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('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('TestTrait', SymbolKind::CLASS_, $this->getDefinitionLocation('TestTrait'), ''),
new SymbolInformation('TestInterface', SymbolKind::INTERFACE, $this->getDefinitionLocation('TestInterface'), ''), new SymbolInformation('TestInterface', SymbolKind::INTERFACE, $this->getDefinitionLocation('TestInterface'), ''),
new SymbolInformation('test_function', SymbolKind::FUNCTION, $this->getDefinitionLocation('test_function()'), ''), 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); ], $result);
// @codingStandardsIgnoreEnd // @codingStandardsIgnoreEnd
} }