Support completion for namespaces
parent
6fb21817e4
commit
5f085a3d8a
|
@ -0,0 +1,5 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SomeNamespace {}
|
||||||
|
|
||||||
|
SomeNa
|
|
@ -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`
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 . '()';
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))),
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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'),
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue