diff --git a/.phan/config.php b/.phan/config.php index a3beb49c..15f600e1 100644 --- a/.phan/config.php +++ b/.phan/config.php @@ -283,9 +283,14 @@ // your application should be included in this list. 'directory_list' => [ 'src', + 'vendor/amphp/amp/lib', + 'vendor/amphp/file/lib', + 'vendor/amphp/socket/src', 'vendor/composer/xdebug-handler/src', 'vendor/felixfbecker/advanced-json-rpc/lib', 'vendor/felixfbecker/language-server-protocol/src/', + 'vendor/league/event/src', + 'vendor/league/uri-parser/src', 'vendor/microsoft/tolerant-php-parser/src', 'vendor/netresearch/jsonmapper/src', 'vendor/phpdocumentor/reflection-common/src', @@ -294,7 +299,6 @@ 'vendor/phpunit/phpunit/src', 'vendor/psr/log/Psr', 'vendor/sabre/event/lib', - 'vendor/sabre/uri/lib', 'vendor/webmozart/glob/src', 'vendor/webmozart/path-util/src', ], diff --git a/.travis.yml b/.travis.yml index da1f1183..018a8ce3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,9 @@ language: php php: - '7.0' + - '7.1' - '7.2' + - '7.3' git: depth: 10 diff --git a/bin/php-language-server.php b/bin/php-language-server.php index 8e5d3487..24ead3bd 100644 --- a/bin/php-language-server.php +++ b/bin/php-language-server.php @@ -1,8 +1,12 @@ critical("Could not connect to language client. Error $errno\n$errstr"); - exit(1); - } - stream_set_blocking($socket, false); - $ls = new LanguageServer( - new ProtocolStreamReader($socket), - new ProtocolStreamWriter($socket) - ); - Loop\run(); + $server = function () use ($logger, $address) { + /** @var ClientSocket $socket */ + $socket = yield Amp\Socket\connect('tcp://' . $address); + $ls = new LanguageServer( + new ProtocolStreamReader($socket), + new ProtocolStreamWriter($socket) + ); + yield $ls->getshutdownDeferred(); + }; } else if (!empty($options['tcp-server'])) { // Run a TCP Server $address = $options['tcp-server']; - $tcpServer = stream_socket_server('tcp://' . $address, $errno, $errstr); - if ($tcpServer === false) { - $logger->critical("Could not listen on $address. Error $errno\n$errstr"); - exit(1); - } - $logger->debug("Server listening on $address"); - $pcntlAvailable = extension_loaded('pcntl'); - if (!$pcntlAvailable) { - $logger->notice('PCNTL is not available. Only a single connection will be accepted'); - } - while ($socket = stream_socket_accept($tcpServer, -1)) { - $logger->debug('Connection accepted'); - stream_set_blocking($socket, false); - if ($pcntlAvailable) { - // If PCNTL is available, fork a child process for the connection - // An exit notification will only terminate the child process - $pid = pcntl_fork(); - if ($pid === -1) { - $logger->critical('Could not fork'); - exit(1); - } else if ($pid === 0) { - // Child process - $reader = new ProtocolStreamReader($socket); - $writer = new ProtocolStreamWriter($socket); - $reader->on('close', function () use ($logger) { - $logger->debug('Connection closed'); - }); - $ls = new LanguageServer($reader, $writer); - Loop\run(); - // Just for safety - exit(0); - } - } else { - // If PCNTL is not available, we only accept one connection. - // An exit notification will terminate the server - $ls = new LanguageServer( - new ProtocolStreamReader($socket), - new ProtocolStreamWriter($socket) - ); - Loop\run(); + $server = function () use ($logger, $address) { + + $server = Amp\Socket\listen('tcp://' . $address); + + $logger->debug("Server listening on $address"); + + while ($socket = yield $server->accept()) { + /** @var ServerSocket $socket */ + list($ip, $port) = \explode(':', $socket->getRemoteAddress()); + + $logger->debug("Accepted connection from {$ip}:{$port}." . PHP_EOL); + + Loop::run(function () use ($socket) { + $ls = new LanguageServer( + new ProtocolStreamReader($socket), + new ProtocolStreamWriter($socket) + ); + yield $ls->getshutdownDeferred(); + }); } - } + }; } else { // Use STDIO $logger->debug('Listening on STDIN'); - stream_set_blocking(STDIN, false); + $inputStream = new ResourceInputStream(STDIN); + $outputStream = new ResourceOutputStream(STDOUT); $ls = new LanguageServer( - new ProtocolStreamReader(STDIN), - new ProtocolStreamWriter(STDOUT) + new ProtocolStreamReader($inputStream), + new ProtocolStreamWriter($outputStream) ); - Loop\run(); + $server = function () use ($ls) { + yield $ls->getshutdownDeferred(); + }; } + +Loop::run($server); diff --git a/composer.json b/composer.json index f63bf3a1..4d5e6ae7 100644 --- a/composer.json +++ b/composer.json @@ -22,16 +22,21 @@ ], "require": { "php": "^7.0", + "amphp/byte-stream": "^1.5", + "amphp/cache": "^1.2", + "amphp/file": "^0.3.5", + "amphp/socket": "^0.10.11", "composer/xdebug-handler": "^1.0", "felixfbecker/advanced-json-rpc": "^3.0.0", "felixfbecker/language-server-protocol": "^1.0.1", "jetbrains/phpstorm-stubs": "dev-master", + "league/event": "^2.2", + "league/uri-parser": "^1.4", "microsoft/tolerant-php-parser": "0.0.*", "netresearch/jsonmapper": "^1.0", + "php-ds/php-ds": "^1.2", "phpdocumentor/reflection-docblock": "^4.0.0", "psr/log": "^1.0", - "sabre/event": "^5.0", - "sabre/uri": "^2.0", "webmozart/glob": "^4.1", "webmozart/path-util": "^2.3" }, diff --git a/src/Cache/Cache.php b/src/Cache/Cache.php index ebc5233d..2705e3e0 100644 --- a/src/Cache/Cache.php +++ b/src/Cache/Cache.php @@ -1,10 +1,8 @@ + * @return \Generator */ - public function get(string $key): Promise; + public function get(string $key): \Generator; /** * Sets a value in the cache * * @param string $key - * @param mixed $value - * @return Promise + * @param mixed $value + * @return \Generator */ - public function set(string $key, $value): Promise; + public function set(string $key, $value): \Generator; } diff --git a/src/Cache/ClientCache.php b/src/Cache/ClientCache.php index e92a3d54..f7b885ed 100644 --- a/src/Cache/ClientCache.php +++ b/src/Cache/ClientCache.php @@ -1,5 +1,5 @@ */ - public function get(string $key): Promise + public function get(string $key): \Generator { - return $this->client->xcache->get($key)->then('unserialize')->otherwise(function () { - // Ignore - }); + $cached = yield from $this->client->xcache->get($key); + $obj = unserialize($cached); + return $obj; } /** * Sets a value in the cache * * @param string $key - * @param mixed $value + * @param mixed $value * @return Promise */ - public function set(string $key, $value): Promise + public function set(string $key, $value): \Generator { - return $this->client->xcache->set($key, serialize($value))->otherwise(function () { - // Ignore - }); + return yield from $this->client->xcache->set($key, serialize($value)); } } diff --git a/src/Cache/FileSystemCache.php b/src/Cache/FileSystemCache.php index d6d44107..38568042 100644 --- a/src/Cache/FileSystemCache.php +++ b/src/Cache/FileSystemCache.php @@ -1,10 +1,8 @@ + * @return \Generator */ - public function get(string $key): Promise + public function get(string $key): \Generator { try { $file = $this->cacheDir . urlencode($key); - if (!file_exists($file)) { - return Promise\resolve(null); - } - return Promise\resolve(unserialize(file_get_contents($file))); + $content = yield \Amp\File\get($file); + return unserialize($content); } catch (\Exception $e) { - return Promise\resolve(null); + return null; } } @@ -49,19 +45,19 @@ public function get(string $key): Promise * Sets a value in the cache * * @param string $key - * @param mixed $value - * @return Promise + * @param mixed $value + * @return \Generator */ - public function set(string $key, $value): Promise + public function set(string $key, $value): \Generator { - try { - $file = $this->cacheDir . urlencode($key); - if (!file_exists($this->cacheDir)) { - mkdir($this->cacheDir); - } - file_put_contents($file, serialize($value)); - } finally { - return Promise\resolve(null); + $file = $this->cacheDir . urlencode($key); + $dir = dirname($file); + if (yield \Amp\File\isfile($dir)) { + yield \Amp\File\unlink($dir); + } + if (!yield \Amp\File\exists($dir)) { + yield \Amp\File\mkdir($dir, 0777, true); } + yield \Amp\File\put($file, serialize($value)); } } diff --git a/src/Client/TextDocument.php b/src/Client/TextDocument.php index 03211fa5..7ae8e4b3 100644 --- a/src/Client/TextDocument.php +++ b/src/Client/TextDocument.php @@ -1,5 +1,5 @@ */ - public function publishDiagnostics(string $uri, array $diagnostics): Promise + public function publishDiagnostics(string $uri, array $diagnostics): \Generator { - return $this->handler->notify('textDocument/publishDiagnostics', [ + yield from $this->handler->notify('textDocument/publishDiagnostics', [ 'uri' => $uri, 'diagnostics' => $diagnostics ]); @@ -51,13 +51,12 @@ public function publishDiagnostics(string $uri, array $diagnostics): Promise * @param TextDocumentIdentifier $textDocument The document to get the content for * @return Promise The document's current content */ - public function xcontent(TextDocumentIdentifier $textDocument): Promise + public function xcontent(TextDocumentIdentifier $textDocument): \Generator { - return $this->handler->request( + $result = yield from $this->handler->request( 'textDocument/xcontent', ['textDocument' => $textDocument] - )->then(function ($result) { - return $this->mapper->map($result, new TextDocumentItem); - }); + ); + return $this->mapper->map($result, new TextDocumentItem); } } diff --git a/src/Client/Window.php b/src/Client/Window.php index c3558f5b..14ac73e1 100644 --- a/src/Client/Window.php +++ b/src/Client/Window.php @@ -41,8 +41,8 @@ public function showMessage(int $type, string $message): Promise * @param string $message * @return Promise */ - public function logMessage(int $type, string $message): Promise + public function logMessage(int $type, string $message): \Generator { - return $this->handler->notify('window/logMessage', ['type' => $type, 'message' => $message]); + yield from $this->handler->notify('window/logMessage', ['type' => $type, 'message' => $message]); } } diff --git a/src/Client/Workspace.php b/src/Client/Workspace.php index 8c31f1f7..bd4b4036 100644 --- a/src/Client/Workspace.php +++ b/src/Client/Workspace.php @@ -1,5 +1,5 @@ Array of documents */ - public function xfiles(string $base = null): Promise + public function xfiles(string $base = null): \Generator { - return $this->handler->request( + $textDocuments = yield from $this->handler->request( 'workspace/xfiles', ['base' => $base] - )->then(function (array $textDocuments) { - return $this->mapper->mapArray($textDocuments, [], TextDocumentIdentifier::class); - }); + ); + return $this->mapper->mapArray($textDocuments, [], TextDocumentIdentifier::class); } } diff --git a/src/Client/XCache.php b/src/Client/XCache.php index b3ce5a5c..fa23fbda 100644 --- a/src/Client/XCache.php +++ b/src/Client/XCache.php @@ -1,5 +1,5 @@ */ - public function get(string $key): Promise + public function get(string $key): \Generator { - return $this->handler->request('xcache/get', ['key' => $key]); + return yield from $this->handler->request('xcache/get', ['key' => $key]); } /** * @param string $key - * @param mixed $value + * @param mixed $value * @return Promise */ - public function set(string $key, $value): Promise + public function set(string $key, $value): \Generator { - return $this->handler->notify('xcache/set', ['key' => $key, 'value' => $value]); + return yield from $this->handler->notify('xcache/set', ['key' => $key, 'value' => $value]); } } diff --git a/src/ClientHandler.php b/src/ClientHandler.php index c98cbc6f..094a292b 100644 --- a/src/ClientHandler.php +++ b/src/ClientHandler.php @@ -1,10 +1,12 @@ Resolved with the result of the request or rejected with an error + * @return \Generator Resolved with the result of the request or rejected with an error */ - public function request(string $method, $params): Promise + public function request(string $method, $params): \Generator { $id = $this->idGenerator->generate(); - return $this->protocolWriter->write( - new Message( - new AdvancedJsonRpc\Request($id, $method, (object)$params) - ) - )->then(function () use ($id) { - $promise = new Promise; - $listener = function (Message $msg) use ($id, $promise, &$listener) { + $deferred = new Deferred(); + $listener = function (MessageEvent $messageEvent) use ($id, $deferred, &$listener) { + $msg = $messageEvent->getMessage(); + Loop::defer(function () use (&$listener, $deferred, $id, $msg) { if (AdvancedJsonRpc\Response::isResponse($msg->body) && $msg->body->id === $id) { // Received a response $this->protocolReader->removeListener('message', $listener); if (AdvancedJsonRpc\SuccessResponse::isSuccessResponse($msg->body)) { - $promise->fulfill($msg->body->result); + $deferred->resolve($msg->body->result); } else { - $promise->reject($msg->body->error); + $deferred->fail($msg->body->error); } } - }; - $this->protocolReader->on('message', $listener); - return $promise; - }); + }); + }; + $this->protocolReader->addListener('message', $listener); + + yield from $this->protocolWriter->write( + new Message( + new AdvancedJsonRpc\Request($id, $method, (object)$params) + ) + ); + + return yield $deferred->promise(); } /** @@ -67,11 +73,11 @@ public function request(string $method, $params): Promise * * @param string $method The method to call * @param array|object $params The method parameters - * @return Promise Will be resolved as soon as the notification has been sent + * @return \Generator Will be resolved as soon as the notification has been sent */ - public function notify(string $method, $params): Promise + public function notify(string $method, $params): \Generator { - return $this->protocolWriter->write( + return yield from $this->protocolWriter->write( new Message( new AdvancedJsonRpc\Notification($method, (object)$params) ) diff --git a/src/ComposerScripts.php b/src/ComposerScripts.php index 2a584d20..7535f744 100644 --- a/src/ComposerScripts.php +++ b/src/ComposerScripts.php @@ -1,16 +1,16 @@ find("$stubsLocation/**/*.php"); + $uris = yield from $finder->find("$stubsLocation/**/*.php"); foreach ($uris as $uri) { echo "Parsing $uri\n"; - $content = yield $contentRetriever->retrieve($uri); + $content = yield from $contentRetriever->retrieve($uri); // Change URI to phpstubs:// - $parts = Uri\parse($uri); + $parts = parse($uri); $parts['path'] = Path::makeRelative($parts['path'], $stubsLocation); $parts['scheme'] = 'phpstubs'; - $uri = Uri\build($parts); // Create a new document and add it to $index - new PhpDocument($uri, $content, $index, $parser, $docBlockFactory, $definitionResolver); + new PhpDocument((string)$uri, $content, $index, $parser, $docBlockFactory, $definitionResolver); } $index->setComplete(); @@ -67,6 +65,6 @@ public static function parseStubs() $index->save(); echo "Finished\n"; - })->wait(); + }); } } diff --git a/src/ContentRetriever/ClientContentRetriever.php b/src/ContentRetriever/ClientContentRetriever.php index d83aad16..a09bb2ff 100644 --- a/src/ContentRetriever/ClientContentRetriever.php +++ b/src/ContentRetriever/ClientContentRetriever.php @@ -1,5 +1,5 @@ Resolved with the content as a string */ - public function retrieve(string $uri): Promise + public function retrieve(string $uri): \Generator { - return $this->client->textDocument->xcontent(new TextDocumentIdentifier($uri)) - ->then(function (TextDocumentItem $textDocument) { - return $textDocument->text; - }); + /** @var TextDocumentItem $textDocument */ + $textDocument = yield from $this->client->textDocument->xcontent(new TextDocumentIdentifier($uri)); + return $textDocument->text; } } diff --git a/src/ContentRetriever/ContentRetriever.php b/src/ContentRetriever/ContentRetriever.php index 4d16b98d..9d926fb1 100644 --- a/src/ContentRetriever/ContentRetriever.php +++ b/src/ContentRetriever/ContentRetriever.php @@ -16,5 +16,5 @@ interface ContentRetriever * @param string $uri The URI of the document * @return Promise Resolved with the content as a string */ - public function retrieve(string $uri): Promise; + public function retrieve(string $uri): \Generator; } diff --git a/src/ContentRetriever/FileSystemContentRetriever.php b/src/ContentRetriever/FileSystemContentRetriever.php index 82e7002d..68b81522 100644 --- a/src/ContentRetriever/FileSystemContentRetriever.php +++ b/src/ContentRetriever/FileSystemContentRetriever.php @@ -17,8 +17,8 @@ class FileSystemContentRetriever implements ContentRetriever * @param string $uri The URI of the document * @return Promise Resolved with the content as a string */ - public function retrieve(string $uri): Promise + public function retrieve(string $uri): \Generator { - return Promise\resolve(file_get_contents(uriToPath($uri))); + return yield \Amp\File\get(uriToPath($uri)); } } diff --git a/src/Event/MessageEvent.php b/src/Event/MessageEvent.php new file mode 100644 index 00000000..3bfbe837 --- /dev/null +++ b/src/Event/MessageEvent.php @@ -0,0 +1,32 @@ +message = $message; + } + + public function getMessage(): Message + { + return $this->message; + } +} diff --git a/src/FilesFinder/ClientFilesFinder.php b/src/FilesFinder/ClientFilesFinder.php index 4315edee..8295bf15 100644 --- a/src/FilesFinder/ClientFilesFinder.php +++ b/src/FilesFinder/ClientFilesFinder.php @@ -1,12 +1,11 @@ The URIs + * @return \Generator The URIs */ - public function find(string $glob): Promise + public function find(string $glob): \Generator { - return $this->client->workspace->xfiles()->then(function (array $textDocuments) use ($glob) { - $uris = []; - foreach ($textDocuments as $textDocument) { - $path = Uri\parse($textDocument->uri)['path']; - if (Glob::match($path, $glob)) { - $uris[] = $textDocument->uri; - } + $textDocuments = yield from $this->client->workspace->xfiles(); + $uris = []; + foreach ($textDocuments as $textDocument) { + $path = parse($textDocument->uri)['path']; + if (Glob::match($path, $glob)) { + $uris[] = $textDocument->uri; } - return $uris; - }); + } + return $uris; } } diff --git a/src/FilesFinder/FileSystemFilesFinder.php b/src/FilesFinder/FileSystemFilesFinder.php index a26b5d89..3bf24e06 100644 --- a/src/FilesFinder/FileSystemFilesFinder.php +++ b/src/FilesFinder/FileSystemFilesFinder.php @@ -1,12 +1,11 @@ + * @return \Amp\Promise */ - public function find(string $glob): Promise + public function find(string $glob): \Generator { - return coroutine(function () use ($glob) { - $uris = []; - foreach (new GlobIterator($glob) as $path) { - // Exclude any directories that also match the glob pattern - if (!is_dir($path)) { - $uris[] = pathToUri($path); + $uris = []; + $basePath = \Webmozart\Glob\Glob::getBasePath($glob); + $pathList = [$basePath]; + while ($pathList) { + $path = array_pop($pathList); + if (yield isdir($path)) { + $subFileList = yield \Amp\File\scandir($path); + foreach ($subFileList as $subFile) { + $pathList[] = $path . DIRECTORY_SEPARATOR . $subFile; } - - yield timeout(); + } elseif (Glob::match($path, $glob)) { + $uris[] = pathToUri($path); } - return $uris; - }); + } + return $uris; } } diff --git a/src/FilesFinder/FilesFinder.php b/src/FilesFinder/FilesFinder.php index 81d6de52..159dc35b 100644 --- a/src/FilesFinder/FilesFinder.php +++ b/src/FilesFinder/FilesFinder.php @@ -1,5 +1,5 @@ */ - public function find(string $glob): Promise; + public function find(string $glob): \Generator; } diff --git a/src/Index/AbstractAggregateIndex.php b/src/Index/AbstractAggregateIndex.php index 8c8c95a1..9c4ee53e 100644 --- a/src/Index/AbstractAggregateIndex.php +++ b/src/Index/AbstractAggregateIndex.php @@ -4,12 +4,10 @@ namespace LanguageServer\Index; use LanguageServer\Definition; -use Sabre\Event\EmitterTrait; +use League\Event\Emitter; -abstract class AbstractAggregateIndex implements ReadableIndex +abstract class AbstractAggregateIndex extends Emitter implements ReadableIndex { - use EmitterTrait; - /** * Returns all indexes managed by the aggregate index * @@ -29,17 +27,17 @@ public function __construct() */ protected function registerIndex(ReadableIndex $index) { - $index->on('complete', function () { + $index->addListener('complete', function () { if ($this->isComplete()) { $this->emit('complete'); } }); - $index->on('static-complete', function () { + $index->addListener('static-complete', function () { if ($this->isStaticComplete()) { $this->emit('static-complete'); } }); - $index->on('definition-added', function () { + $index->addListener('definition-added', function () { $this->emit('definition-added'); }); } diff --git a/src/Index/DependenciesIndex.php b/src/Index/DependenciesIndex.php index 059cb7dd..8bd0ff89 100644 --- a/src/Index/DependenciesIndex.php +++ b/src/Index/DependenciesIndex.php @@ -3,6 +3,13 @@ namespace LanguageServer\Index; +use League\Event\EmitterInterface; +use League\Event\EventInterface; +use League\Event\GeneratorInterface; +use League\Event\ListenerAcceptorInterface; +use League\Event\ListenerInterface; +use League\Event\ListenerProviderInterface; + class DependenciesIndex extends AbstractAggregateIndex { /** diff --git a/src/Index/Index.php b/src/Index/Index.php index 0d61f9ca..07d831ee 100644 --- a/src/Index/Index.php +++ b/src/Index/Index.php @@ -1,18 +1,18 @@ setStaticComplete(); } $this->complete = true; - $this->emit('complete'); } /** @@ -72,7 +71,6 @@ public function setComplete() public function setStaticComplete() { $this->staticComplete = true; - $this->emit('static-complete'); } /** @@ -131,9 +129,9 @@ public function getChildDefinitionsForFqn(string $fqn): \Generator continue; } if ($item instanceof Definition) { - yield $fqn.$name => $item; + yield $fqn . $name => $item; } elseif (is_array($item) && isset($item[''])) { - yield $fqn.$name => $item['']; + yield $fqn . $name => $item['']; } } } @@ -173,8 +171,6 @@ public function setDefinition(string $fqn, Definition $definition) { $parts = $this->splitFqn($fqn); $this->indexDefinition(0, $parts, $this->definitions, $definition); - - $this->emit('definition-added'); } /** @@ -200,8 +196,10 @@ public function removeDefinition(string $fqn) */ public function getReferenceUris(string $fqn): \Generator { - foreach ($this->references[$fqn] ?? [] as $uri) { - yield $uri; + if (isset($this->references[$fqn])) { + foreach ($this->references[$fqn] as $uri) { + yield $uri; + } } } @@ -209,7 +207,7 @@ public function getReferenceUris(string $fqn): \Generator * For test use. * Returns all references, keyed by fqn. * - * @return string[][] + * @return Set[] */ public function getReferences(): array { @@ -225,12 +223,9 @@ public function getReferences(): array public function addReferenceUri(string $fqn, string $uri) { if (!isset($this->references[$fqn])) { - $this->references[$fqn] = []; - } - // TODO: use DS\Set instead of searching array - if (array_search($uri, $this->references[$fqn], true) === false) { - $this->references[$fqn][] = $uri; + $this->references[$fqn] = new Set(); } + $this->references[$fqn]->add($uri); } /** @@ -245,11 +240,7 @@ public function removeReferenceUri(string $fqn, string $uri) if (!isset($this->references[$fqn])) { return; } - $index = array_search($fqn, $this->references[$fqn], true); - if ($index === false) { - return; - } - array_splice($this->references[$fqn], $index, 1); + $this->references[$fqn]->remove($uri); } /** @@ -299,9 +290,9 @@ private function yieldDefinitionsRecursively(array &$storage, string $prefix = ' { foreach ($storage as $key => $value) { if (!is_array($value)) { - yield $prefix.$key => $value; + yield $prefix . $key => $value; } else { - yield from $this->yieldDefinitionsRecursively($value, $prefix.$key); + yield from $this->yieldDefinitionsRecursively($value, $prefix . $key); } } } @@ -365,8 +356,8 @@ private function splitFqn(string $fqn): array * It can be an index node or a Definition if the $parts are precise * enough. Returns null when nothing is found. * - * @param string[] $path The splitted FQN - * @param array|Definition &$storage The current level to look for $path. + * @param string[] $path The splitted FQN + * @param array|Definition &$storage The current level to look for $path. * @return array|Definition|null */ private function getIndexValue(array $path, &$storage) @@ -389,10 +380,10 @@ private function getIndexValue(array $path, &$storage) * Recursive function that stores the given Definition in the given $storage array represented * as a tree matching the given $parts. * - * @param int $level The current level of FQN part - * @param string[] $parts The splitted FQN - * @param array &$storage The array in which to store the $definition - * @param Definition $definition The Definition to store + * @param int $level The current level of FQN part + * @param string[] $parts The splitted FQN + * @param array &$storage The array in which to store the $definition + * @param Definition $definition The Definition to store */ private function indexDefinition(int $level, array $parts, array &$storage, Definition $definition) { @@ -416,10 +407,10 @@ private function indexDefinition(int $level, array $parts, array &$storage, Defi * $storage array. The function also looks up recursively to remove the parents of the * definition which no longer has children to avoid to let empty arrays in the index. * - * @param int $level The current level of FQN part - * @param string[] $parts The splitted FQN - * @param array &$storage The current array in which to remove data - * @param array &$rootStorage The root storage array + * @param int $level The current level of FQN part + * @param string[] $parts The splitted FQN + * @param array &$storage The current array in which to remove data + * @param array &$rootStorage The root storage array */ private function removeIndexedDefinition(int $level, array $parts, array &$storage, array &$rootStorage) { @@ -429,7 +420,7 @@ private function removeIndexedDefinition(int $level, array $parts, array &$stora if (isset($storage[$part])) { unset($storage[$part]); - if (0 === count($storage)) { + if (0 === count($storage) && $level != 0) { // parse again the definition tree to remove the parent // when it has no more children $this->removeIndexedDefinition(0, array_slice($parts, 0, $level), $rootStorage, $rootStorage); diff --git a/src/Index/ReadableIndex.php b/src/Index/ReadableIndex.php index 505bb9a9..8f68a706 100644 --- a/src/Index/ReadableIndex.php +++ b/src/Index/ReadableIndex.php @@ -4,7 +4,7 @@ namespace LanguageServer\Index; use LanguageServer\Definition; -use Sabre\Event\EmitterInterface; +use League\Event\EmitterInterface; /** * The ReadableIndex interface provides methods to lookup definitions and references diff --git a/src/Indexer.php b/src/Indexer.php index 7ebff3f4..b13cc785 100644 --- a/src/Indexer.php +++ b/src/Indexer.php @@ -1,15 +1,15 @@ */ - public function index(): Promise + public function index(): \Generator { - return coroutine(function () { - - $pattern = Path::makeAbsolute('**/*.php', $this->rootPath); - $uris = yield $this->filesFinder->find($pattern); - - $count = count($uris); - $startTime = microtime(true); - $this->client->window->logMessage(MessageType::INFO, "$count files total"); - - /** @var string[] */ - $source = []; - /** @var string[][] */ - $deps = []; - - foreach ($uris as $uri) { - $packageName = getPackageName($uri, $this->composerJson); - if ($this->composerLock !== null && $packageName) { - // Dependency file - if (!isset($deps[$packageName])) { - $deps[$packageName] = []; - } - $deps[$packageName][] = $uri; - } else { - // Source file - $source[] = $uri; + $pattern = Path::makeAbsolute('**/*.php', $this->rootPath); + $uris = yield from $this->filesFinder->find($pattern); + + $count = count($uris); + $startTime = microtime(true); + yield from $this->client->window->logMessage(MessageType::INFO, "$count files total"); + + /** @var string[] */ + $source = []; + /** @var string[][] */ + $deps = []; + + foreach ($uris as $uri) { + $packageName = getPackageName($uri, $this->composerJson); + if ($this->composerLock !== null && $packageName) { + // Dependency file + if (!isset($deps[$packageName])) { + $deps[$packageName] = []; } + $deps[$packageName][] = $uri; + } else { + // Source file + $source[] = $uri; } - - // Index source - // Definitions and static references - $this->client->window->logMessage(MessageType::INFO, 'Indexing project for definitions and static references'); - yield $this->indexFiles($source); - $this->sourceIndex->setStaticComplete(); - // Dynamic references - $this->client->window->logMessage(MessageType::INFO, 'Indexing project for dynamic references'); - yield $this->indexFiles($source); - $this->sourceIndex->setComplete(); - - // Index dependencies - $this->client->window->logMessage(MessageType::INFO, count($deps) . ' Packages'); - foreach ($deps as $packageName => $files) { - // Find version of package and check cache - $packageKey = null; - $cacheKey = null; - $index = null; - foreach (array_merge($this->composerLock->packages, (array)$this->composerLock->{'packages-dev'}) as $package) { - // Check if package name matches and version is absolute - // Dynamic constraints are not cached, because they can change every time - $packageVersion = ltrim($package->version, 'v'); - if ($package->name === $packageName && strpos($packageVersion, 'dev') === false) { - $packageKey = $packageName . ':' . $packageVersion; - $cacheKey = self::CACHE_VERSION . ':' . $packageKey; - // Check cache - $index = yield $this->cache->get($cacheKey); - break; - } + } + + // Index source + // Definitions and static references + yield from $this->client->window->logMessage(MessageType::INFO, 'Indexing project for definitions and static references'); + yield from $this->indexFiles($source); + $this->sourceIndex->setStaticComplete(); + // Dynamic references + yield from $this->client->window->logMessage(MessageType::INFO, 'Indexing project for dynamic references'); + yield from $this->indexFiles($source); + $this->sourceIndex->setComplete(); + + // Index dependencies + yield from $this->client->window->logMessage(MessageType::INFO, count($deps) . ' Packages'); + foreach ($deps as $packageName => $files) { + // Find version of package and check cache + $packageKey = null; + $cacheKey = null; + $index = null; + foreach (array_merge($this->composerLock->packages, (array)$this->composerLock->{'packages-dev'}) as $package) { + // Check if package name matches and version is absolute + // Dynamic constraints are not cached, because they can change every time + $packageVersion = ltrim($package->version, 'v'); + if ($package->name === $packageName && strpos($packageVersion, 'dev') === false) { + $packageKey = $packageName . ':' . $packageVersion; + $cacheKey = self::CACHE_VERSION . ':' . $packageKey; + // Check cache + $index = yield from $this->cache->get($cacheKey); + break; } - if ($index !== null) { - // Cache hit - $this->dependenciesIndex->setDependencyIndex($packageName, $index); - $this->client->window->logMessage(MessageType::INFO, "Restored $packageKey from cache"); + } + $index = null; + if ($index !== null) { + // Cache hit + $this->dependenciesIndex->setDependencyIndex($packageName, $index); + yield from $this->client->window->logMessage(MessageType::INFO, "Restored $packageKey from cache"); + } else { + // Cache miss + $index = $this->dependenciesIndex->getDependencyIndex($packageName); + + // Index definitions and static references + yield from $this->client->window->logMessage(MessageType::INFO, 'Indexing ' . ($packageKey ?? $packageName) . ' for definitions and static references'); + yield from $this->indexFiles($files); + $index->setStaticComplete(); + + // Index dynamic references + yield from $this->client->window->logMessage(MessageType::INFO, 'Indexing ' . ($packageKey ?? $packageName) . ' for dynamic references'); + yield from $this->indexFiles($files); + $index->setComplete(); + + // If we know the version (cache key), save index for the dependency in the cache + if ($cacheKey !== null) { + yield from $this->client->window->logMessage(MessageType::INFO, "Storing $packageKey in cache"); + yield from $this->cache->set($cacheKey, $index); } else { - // Cache miss - $index = $this->dependenciesIndex->getDependencyIndex($packageName); - - // Index definitions and static references - $this->client->window->logMessage(MessageType::INFO, 'Indexing ' . ($packageKey ?? $packageName) . ' for definitions and static references'); - yield $this->indexFiles($files); - $index->setStaticComplete(); - - // Index dynamic references - $this->client->window->logMessage(MessageType::INFO, 'Indexing ' . ($packageKey ?? $packageName) . ' for dynamic references'); - yield $this->indexFiles($files); - $index->setComplete(); - - // If we know the version (cache key), save index for the dependency in the cache - if ($cacheKey !== null) { - $this->client->window->logMessage(MessageType::INFO, "Storing $packageKey in cache"); - $this->cache->set($cacheKey, $index); - } else { - $this->client->window->logMessage(MessageType::WARNING, "Could not compute cache key for $packageName"); - } + yield from $this->client->window->logMessage(MessageType::WARNING, "Could not compute cache key for $packageName"); } + echo PHP_EOL; } - - $duration = (int)(microtime(true) - $startTime); - $mem = (int)(memory_get_usage(true) / (1024 * 1024)); - $this->client->window->logMessage( - MessageType::INFO, - "All $count PHP files parsed in $duration seconds. $mem MiB allocated." - ); - }); + } + + $duration = (int)(microtime(true) - $startTime); + $mem = (int)(memory_get_usage(true) / (1024 * 1024)); + yield from $this->client->window->logMessage( + MessageType::INFO, + "All $count PHP files parsed in $duration seconds. $mem MiB allocated." + ); } /** * @param array $files * @return Promise */ - private function indexFiles(array $files): Promise + private function indexFiles(array $files): \Generator { - return coroutine(function () use ($files) { - foreach ($files as $i => $uri) { - // Skip open documents - if ($this->documentLoader->isOpen($uri)) { - continue; - } + foreach ($files as $i => $uri) { + // Skip open documents + if ($this->documentLoader->isOpen($uri)) { + continue; + } - // Give LS to the chance to handle requests while indexing - yield timeout(); - $this->client->window->logMessage(MessageType::LOG, "Parsing $uri"); - try { - $document = yield $this->documentLoader->load($uri); - if (!isVendored($document, $this->composerJson)) { - $this->client->textDocument->publishDiagnostics($uri, $document->getDiagnostics()); - } - } catch (ContentTooLargeException $e) { - $this->client->window->logMessage( - MessageType::INFO, - "Ignoring file {$uri} because it exceeds size limit of {$e->limit} bytes ({$e->size})" - ); - } catch (\Exception $e) { - $this->client->window->logMessage(MessageType::ERROR, "Error parsing $uri: " . (string)$e); + // Give LS to the chance to handle requests while indexing + yield new Delayed(0); + yield from $this->client->window->logMessage(MessageType::LOG, "Parsing $uri"); + try { + $document = yield from $this->documentLoader->load($uri); + if (!isVendored($document, $this->composerJson)) { + yield from $this->client->textDocument->publishDiagnostics($uri, $document->getDiagnostics()); } + } catch (ContentTooLargeException $e) { + yield from $this->client->window->logMessage( + MessageType::INFO, + "Ignoring file {$uri} because it exceeds size limit of {$e->limit} bytes ({$e->size})" + ); + } catch (\Exception $e) { + yield from $this->client->window->logMessage(MessageType::ERROR, "Error parsing $uri: " . (string)$e); } - }); + } } } diff --git a/src/LanguageServer.php b/src/LanguageServer.php index 38dfeb1e..6c4ca8d8 100644 --- a/src/LanguageServer.php +++ b/src/LanguageServer.php @@ -1,24 +1,24 @@ shutdownDeferred = new Deferred(); + $this->protocolReader = $reader; - $this->protocolReader->on('close', function () { + $this->protocolReader->addListener('close', function () { $this->shutdown(); - $this->exit(); }); - $this->protocolReader->on('message', function (Message $msg) { - coroutine(function () use ($msg) { + $this->protocolWriter = $writer; + $this->client = new LanguageClient($reader, $writer); + $this->protocolReader->addListener('message', function (MessageEvent $messageEvent) use ($reader, $writer) { + $msg = $messageEvent->getMessage(); + Loop::defer(function () use ($msg) { // Ignore responses, this is the handler for requests and notifications if (AdvancedJsonRpc\Response::isResponse($msg->body)) { return; @@ -149,12 +154,15 @@ public function __construct(ProtocolReader $reader, ProtocolWriter $writer) } else { $responseBody = new AdvancedJsonRpc\SuccessResponse($msg->body->id, $result); } - $this->protocolWriter->write(new Message($responseBody)); + yield from $this->protocolWriter->write(new Message($responseBody)); } - })->otherwise('\\LanguageServer\\crash'); + }); }); - $this->protocolWriter = $writer; - $this->client = new LanguageClient($reader, $writer); + } + + public function getshutdownDeferred(): Promise + { + return $this->shutdownDeferred->promise(); } /** @@ -163,15 +171,13 @@ public function __construct(ProtocolReader $reader, ProtocolWriter $writer) * @param ClientCapabilities $capabilities The capabilities provided by the client (editor) * @param string|null $rootPath The rootPath of the workspace. Is null if no folder is open. * @param int|null $processId The process Id of the parent process that started the server. Is null if the process has not been started by another process. If the parent process is not alive then the server should exit (see exit notification) its process. + * @param string|null $rootUri * @return Promise */ public function initialize(ClientCapabilities $capabilities, string $rootPath = null, int $processId = null, string $rootUri = null): Promise { - if ($rootPath === null && $rootUri !== null) { - $rootPath = uriToPath($rootUri); - } - return coroutine(function () use ($capabilities, $rootPath, $processId) { - + $deferred = new Deferred(); + Loop::defer(function () use ($deferred, $capabilities, $rootPath, $processId, $rootUri) { if ($capabilities->xfilesProvider) { $this->filesFinder = new ClientFilesFinder($this->client); } else { @@ -200,25 +206,25 @@ public function initialize(ClientCapabilities $capabilities, string $rootPath = ); if ($rootPath !== null) { - yield $this->beforeIndex($rootPath); + yield from $this->beforeIndex($rootPath); // Find composer.json if ($this->composerJson === null) { - $composerJsonFiles = yield $this->filesFinder->find(Path::makeAbsolute('**/composer.json', $rootPath)); + $composerJsonFiles = yield from $this->filesFinder->find(Path::makeAbsolute('**/composer.json', $rootPath)); sortUrisLevelOrder($composerJsonFiles); if (!empty($composerJsonFiles)) { - $this->composerJson = json_decode(yield $this->contentRetriever->retrieve($composerJsonFiles[0])); + $this->composerJson = json_decode(yield from $this->contentRetriever->retrieve($composerJsonFiles[0])); } } // Find composer.lock if ($this->composerLock === null) { - $composerLockFiles = yield $this->filesFinder->find(Path::makeAbsolute('**/composer.lock', $rootPath)); + $composerLockFiles = yield from $this->filesFinder->find(Path::makeAbsolute('**/composer.lock', $rootPath)); sortUrisLevelOrder($composerLockFiles); if (!empty($composerLockFiles)) { - $this->composerLock = json_decode(yield $this->contentRetriever->retrieve($composerLockFiles[0])); + $this->composerLock = json_decode(yield from $this->contentRetriever->retrieve($composerLockFiles[0])); } } @@ -236,7 +242,9 @@ public function initialize(ClientCapabilities $capabilities, string $rootPath = $this->composerLock, $this->composerJson ); - $indexer->index()->otherwise('\\LanguageServer\\crash'); + Loop::defer(function () use ($indexer) { + yield from $indexer->index(); + }); } @@ -288,8 +296,9 @@ public function initialize(ClientCapabilities $capabilities, string $rootPath = $serverCapabilities->xdefinitionProvider = true; $serverCapabilities->xdependenciesProvider = true; - return new InitializeResult($serverCapabilities); + $deferred->resolve(new InitializeResult($serverCapabilities)); }); + return $deferred->promise(); } /** @@ -297,29 +306,23 @@ public function initialize(ClientCapabilities $capabilities, string $rootPath = * (otherwise the response might not be delivered correctly to the client). There is a separate exit notification that * asks the server to exit. * - * @return void + * @return \Generator */ public function shutdown() { unset($this->project); - } - - /** - * A notification to ask the server to exit its process. - * - * @return void - */ - public function exit() - { - exit(0); + $this->shutdownDeferred->resolve(); + yield new Delayed(0); } /** * Called before indexing, can return a Promise * * @param string $rootPath + * @return \Generator */ protected function beforeIndex(string $rootPath) { + yield new Delayed(0, null); } } diff --git a/src/Message.php b/src/Message.php index fb511102..051182af 100644 --- a/src/Message.php +++ b/src/Message.php @@ -1,10 +1,9 @@ body = MessageBody::parse(array_pop($parts)); foreach ($parts as $line) { if ($line) { @@ -55,11 +54,11 @@ public function __toString(): string { $body = (string)$this->body; $contentLength = strlen($body); - $this->headers['Content-Length'] = $contentLength; + $this->headers['Content-Length'] = $contentLength + 6; $headers = ''; foreach ($this->headers as $name => $value) { $headers .= "$name: $value\r\n"; } - return $headers . "\r\n" . $body; + return $headers . "\r\n" . $body . "\r\n\r\n\r\n"; } } diff --git a/src/PhpDocument.php b/src/PhpDocument.php index d148f9c5..28ddbd81 100644 --- a/src/PhpDocument.php +++ b/src/PhpDocument.php @@ -1,5 +1,5 @@ PhpDocument of open documents that should be kept in memory * - * @var PhpDocument + * @var PhpDocument[] */ private $documents = []; @@ -37,11 +36,6 @@ class PhpDocumentLoader */ private $parser; - /** - * @var PhpParser\Parser - */ - private $tolerantParser; - /** * @var DocBlockFactory */ @@ -87,11 +81,16 @@ public function get(string $uri) * If the document is not open, loads it. * * @param string $uri - * @return Promise + * @return \Generator + * @throws ContentTooLargeException */ - public function getOrLoad(string $uri): Promise + public function getOrLoad(string $uri): \Generator { - return isset($this->documents[$uri]) ? Promise\resolve($this->documents[$uri]) : $this->load($uri); + if (isset($this->documents[$uri])) { + return $this->documents[$uri]; + } else { + return yield from $this->load($uri); + } } /** @@ -100,27 +99,25 @@ public function getOrLoad(string $uri): Promise * The document is NOT added to the list of open documents, but definitions are registered. * * @param string $uri - * @return Promise + * @return \Generator + * @throws ContentTooLargeException */ - public function load(string $uri): Promise + public function load(string $uri): \Generator { - return coroutine(function () use ($uri) { - - $limit = 150000; - $content = yield $this->contentRetriever->retrieve($uri); - $size = strlen($content); - if ($size > $limit) { - throw new ContentTooLargeException($uri, $size, $limit); - } - - if (isset($this->documents[$uri])) { - $document = $this->documents[$uri]; - $document->updateContent($content); - } else { - $document = $this->create($uri, $content); - } - return $document; - }); + $limit = 150000; + $content = yield from $this->contentRetriever->retrieve($uri); + $size = strlen($content); + if ($size > $limit) { + throw new ContentTooLargeException($uri, $size, $limit); + } + + if (isset($this->documents[$uri])) { + $document = $this->documents[$uri]; + $document->updateContent($content); + } else { + $document = $this->create($uri, $content); + } + return $document; } /** @@ -147,7 +144,7 @@ public function create(string $uri, string $content): PhpDocument * * @param string $uri * @param string $content - * @return void + * @return PhpDocument */ public function open(string $uri, string $content) { diff --git a/src/ProtocolReader.php b/src/ProtocolReader.php index c199fba1..d9a0d4ab 100644 --- a/src/ProtocolReader.php +++ b/src/ProtocolReader.php @@ -1,9 +1,9 @@ input = $input; - - $this->on('close', function () { - Loop\removeReadStream($this->input); - }); - - Loop\addReadStream($this->input, function () { - if (feof($this->input)) { - // If stream_select reported a status change for this stream, - // but the stream is EOF, it means it was closed. - $this->emit('close'); - return; - } - while (($c = fgetc($this->input)) !== false && $c !== '') { - $this->buffer .= $c; - switch ($this->parsingMode) { - case self::PARSE_HEADERS: - if ($this->buffer === "\r\n") { - $this->parsingMode = self::PARSE_BODY; - $this->contentLength = (int)$this->headers['Content-Length']; - $this->buffer = ''; - } else if (substr($this->buffer, -2) === "\r\n") { - $parts = explode(':', $this->buffer); - $this->headers[$parts[0]] = trim($parts[1]); - $this->buffer = ''; - } - break; - case self::PARSE_BODY: - if (strlen($this->buffer) === $this->contentLength) { - $msg = new Message(MessageBody::parse($this->buffer), $this->headers); - $this->emit('message', [$msg]); - $this->parsingMode = self::PARSE_HEADERS; - $this->headers = []; - $this->buffer = ''; + Loop::defer(function () use (&$input) { + $buffer = ''; + while (true) { + $headers = []; + while (true) { + while (($pos = strpos($buffer, "\r\n")) === false) { + $read = yield $input->read(); + if ($read === null) { + return; } + $buffer .= $read; + } + + $headerLine = substr($buffer, 0, $pos); + $buffer = substr($buffer, (int)$pos + 2); + if (!$headerLine) { break; + } + $headerPairs = \explode(': ', $headerLine); + $headers[$headerPairs[0]] = $headerPairs[1]; + } + $contentLength = (int)$headers['Content-Length']; + while (strlen($buffer) < $contentLength) { + $read = yield $this->input->read(); + if ($read === null) { + return; + } + $buffer .= $read; } + $body = substr($buffer, 0, $contentLength); + $buffer = substr($buffer, $contentLength); + $msg = new Message(MessageBody::parse($body), $headers); + $this->emit(new MessageEvent('message', $msg)); } }); } diff --git a/src/ProtocolStreamWriter.php b/src/ProtocolStreamWriter.php index 166ea906..e4712cce 100644 --- a/src/ProtocolStreamWriter.php +++ b/src/ProtocolStreamWriter.php @@ -1,8 +1,9 @@ output = $output; } @@ -32,21 +33,9 @@ public function __construct($output) /** * {@inheritdoc} */ - public function write(Message $msg): Promise + public function write(Message $msg): \Generator { - // if the message queue is currently empty, register a write handler. - if (empty($this->messages)) { - Loop\addWriteStream($this->output, function () { - $this->flush(); - }); - } - - $promise = new Promise(); - $this->messages[] = [ - 'message' => (string)$msg, - 'promise' => $promise - ]; - return $promise; + yield $this->output->write((string)$msg); } /** diff --git a/src/ProtocolWriter.php b/src/ProtocolWriter.php index ac252180..005f412d 100644 --- a/src/ProtocolWriter.php +++ b/src/ProtocolWriter.php @@ -14,5 +14,5 @@ interface ProtocolWriter * @param Message $msg * @return Promise Resolved when the message has been fully written out to the output stream */ - public function write(Message $msg): Promise; + public function write(Message $msg): \Generator; } diff --git a/src/Server/TextDocument.php b/src/Server/TextDocument.php index 039ff578..a7ac5f35 100644 --- a/src/Server/TextDocument.php +++ b/src/Server/TextDocument.php @@ -1,37 +1,35 @@ documentLoader->getOrLoad($textDocument->uri)->then(function (PhpDocument $document) { + $deferred = new Deferred(); + Loop::defer(function () use ($textDocument, $deferred) { + /** @var PhpDocument $document */ + $document = yield from $this->documentLoader->getOrLoad($textDocument->uri); + $symbols = []; foreach ($document->getDefinitions() as $fqn => $definition) { $symbols[] = $definition->symbolInformation; } - return $symbols; + $deferred->resolve($symbols); }); + return $deferred->promise(); } /** @@ -134,10 +137,12 @@ public function documentSymbol(TextDocumentIdentifier $textDocument): Promise */ public function didOpen(TextDocumentItem $textDocument) { - $document = $this->documentLoader->open($textDocument->uri, $textDocument->text); - if (!isVendored($document, $this->composerJson)) { - $this->client->textDocument->publishDiagnostics($textDocument->uri, $document->getDiagnostics()); - } + Loop::defer(function () use ($textDocument) { + $document = $this->documentLoader->open($textDocument->uri, $textDocument->text); + if (!isVendored($document, $this->composerJson)) { + yield from $this->client->textDocument->publishDiagnostics($textDocument->uri, $document->getDiagnostics()); + } + }); } /** @@ -145,13 +150,18 @@ public function didOpen(TextDocumentItem $textDocument) * * @param \LanguageServerProtocol\VersionedTextDocumentIdentifier $textDocument * @param \LanguageServerProtocol\TextDocumentContentChangeEvent[] $contentChanges - * @return void + * @return Promise */ public function didChange(VersionedTextDocumentIdentifier $textDocument, array $contentChanges) { - $document = $this->documentLoader->get($textDocument->uri); - $document->updateContent($contentChanges[0]->text); - $this->client->textDocument->publishDiagnostics($textDocument->uri, $document->getDiagnostics()); + $deferred = new Deferred(); + Loop::defer(function () use ($deferred, $textDocument, $contentChanges) { + $document = $this->documentLoader->get($textDocument->uri); + $document->updateContent($contentChanges[0]->text); + yield from $this->client->textDocument->publishDiagnostics($textDocument->uri, $document->getDiagnostics()); + $deferred->resolve(); + }); + return $deferred->promise(); } /** @@ -179,8 +189,9 @@ public function references( TextDocumentIdentifier $textDocument, Position $position ): Promise { - return coroutine(function () use ($textDocument, $position) { - $document = yield $this->documentLoader->getOrLoad($textDocument->uri); + $deferred = new Deferred(); + Loop::defer(function () use ($deferred, $textDocument, $position) { + $document = yield from $this->documentLoader->getOrLoad($textDocument->uri); $node = $document->getNodeAtPosition($position); if ($node === null) { return []; @@ -189,8 +200,7 @@ public function references( // Variables always stay in the boundary of the file and need to be searched inside their function scope // by traversing the AST if ( - - ($node instanceof Node\Expression\Variable && !($node->getParent()->getParent() instanceof Node\PropertyDeclaration)) + ($node instanceof Node\Expression\Variable && !($node->getParent()->getParent() instanceof Node\PropertyDeclaration)) || $node instanceof Node\Parameter || $node instanceof Node\UseVariableName ) { @@ -217,21 +227,18 @@ public function references( // Definition with a global FQN $fqn = DefinitionResolver::getDefinedFqn($node); - // Wait until indexing finished - if (!$this->index->isComplete()) { - yield waitForEvent($this->index, 'complete'); - } if ($fqn === null) { $fqn = $this->definitionResolver->resolveReferenceNodeToFqn($node); if ($fqn === null) { - return []; + $deferred->resolve([]); + return; } } $refDocumentPromises = []; foreach ($this->index->getReferenceUris($fqn) as $uri) { - $refDocumentPromises[] = $this->documentLoader->getOrLoad($uri); + $refDocumentPromises[] = new Coroutine($this->documentLoader->getOrLoad($uri)); } - $refDocuments = yield Promise\all($refDocumentPromises); + $refDocuments = yield \Amp\Promise\all($refDocumentPromises); foreach ($refDocuments as $document) { $refs = $document->getReferenceNodesByFqn($fqn); if ($refs !== null) { @@ -241,8 +248,9 @@ public function references( } } } - return $locations; + $deferred->resolve($locations); }); + return $deferred->promise(); } /** @@ -250,16 +258,20 @@ public function references( * cursor position. * * @param TextDocumentIdentifier $textDocument The text document - * @param Position $position The position inside the text document + * @param Position $position The position inside the text document * * @return Promise */ public function signatureHelp(TextDocumentIdentifier $textDocument, Position $position): Promise { - return coroutine(function () use ($textDocument, $position) { - $document = yield $this->documentLoader->getOrLoad($textDocument->uri); - return $this->signatureHelpProvider->getSignatureHelp($document, $position); + $deferred = new Deferred(); + Loop::defer(function () use ($deferred, $textDocument, $position) { + $document = yield from $this->documentLoader->getOrLoad($textDocument->uri); + $deferred->resolve( + yield from $this->signatureHelpProvider->getSignatureHelp($document, $position) + ); }); + return $deferred->promise(); } /** @@ -269,11 +281,13 @@ public function signatureHelp(TextDocumentIdentifier $textDocument, Position $po * @param TextDocumentIdentifier $textDocument The text document * @param Position $position The position inside the text document * @return Promise + * @throws \LanguageServer\ContentTooLargeException */ public function definition(TextDocumentIdentifier $textDocument, Position $position): Promise { - return coroutine(function () use ($textDocument, $position) { - $document = yield $this->documentLoader->getOrLoad($textDocument->uri); + $deferred = new Deferred(); + Loop::defer(function () use ($deferred, $textDocument, $position) { + $document = yield from $this->documentLoader->getOrLoad($textDocument->uri); $node = $document->getNodeAtPosition($position); if ($node === null) { return []; @@ -291,17 +305,18 @@ public function definition(TextDocumentIdentifier $textDocument, Position $posit if ($def !== null || $this->index->isComplete()) { break; } - yield waitForEvent($this->index, 'definition-added'); } if ( $def === null || $def->symbolInformation === null - || Uri\parse($def->symbolInformation->location->uri)['scheme'] === 'phpstubs' + || parse($def->symbolInformation->location->uri)['scheme'] === 'phpstubs' ) { - return []; + $deferred->resolve([]); + } else { + $deferred->resolve($def->symbolInformation->location); } - return $def->symbolInformation->location; }); + return $deferred->promise(); } /** @@ -313,8 +328,9 @@ public function definition(TextDocumentIdentifier $textDocument, Position $posit */ public function hover(TextDocumentIdentifier $textDocument, Position $position): Promise { - return coroutine(function () use ($textDocument, $position) { - $document = yield $this->documentLoader->getOrLoad($textDocument->uri); + $deferred = new Deferred(); + Loop::defer(function () use ($deferred, $textDocument, $position) { + $document = yield from $this->documentLoader->getOrLoad($textDocument->uri); // Find the node under the cursor $node = $document->getNodeAtPosition($position); if ($node === null) { @@ -333,7 +349,6 @@ public function hover(TextDocumentIdentifier $textDocument, Position $position): if ($def !== null || $this->index->isComplete()) { break; } - yield waitForEvent($this->index, 'definition-added'); } $range = RangeFactory::fromNode($node); if ($def === null) { @@ -346,8 +361,9 @@ public function hover(TextDocumentIdentifier $textDocument, Position $position): if ($def->documentation) { $contents[] = $def->documentation; } - return new Hover($contents, $range); + $deferred->resolve(new Hover($contents, $range)); }); + return $deferred->promise(); } /** @@ -360,17 +376,20 @@ public function hover(TextDocumentIdentifier $textDocument, Position $position): * interface then a 'completionItem/resolve' request is sent with the selected completion item as a param. The * returned completion item should have the documentation property filled in. * - * @param TextDocumentIdentifier The text document + * @param TextDocumentIdentifier $textDocument * @param Position $position The position * @param CompletionContext|null $context The completion context * @return Promise */ public function completion(TextDocumentIdentifier $textDocument, Position $position, CompletionContext $context = null): Promise { - return coroutine(function () use ($textDocument, $position, $context) { - $document = yield $this->documentLoader->getOrLoad($textDocument->uri); - return $this->completionProvider->provideCompletion($document, $position, $context); + $deferred = new Deferred(); + Loop::defer(function () use ($deferred, $context, $position, $textDocument) { + /** @var PhpDocument $document */ + $document = yield from $this->documentLoader->getOrLoad($textDocument->uri); + $deferred->resolve($this->completionProvider->provideCompletion($document, $position, $context)); }); + return $deferred->promise(); } /** @@ -382,12 +401,13 @@ public function completion(TextDocumentIdentifier $textDocument, Position $posit * but still may know some information about it. * * @param TextDocumentIdentifier $textDocument The text document - * @param Position $position The position inside the text document + * @param Position $position The position inside the text document * @return Promise */ public function xdefinition(TextDocumentIdentifier $textDocument, Position $position): Promise { - return coroutine(function () use ($textDocument, $position) { + $deferred = new Deferred(); + Loop::defer(function () use ($deferred, $textDocument, $position) { $document = yield $this->documentLoader->getOrLoad($textDocument->uri); $node = $document->getNodeAtPosition($position); if ($node === null) { @@ -406,12 +426,11 @@ public function xdefinition(TextDocumentIdentifier $textDocument, Position $posi if ($def !== null || $this->index->isComplete()) { break; } - yield waitForEvent($this->index, 'definition-added'); } if ( $def === null || $def->symbolInformation === null - || Uri\parse($def->symbolInformation->location->uri)['scheme'] === 'phpstubs' + || parse($def->symbolInformation->location->uri)['scheme'] === 'phpstubs' ) { return []; } @@ -422,7 +441,8 @@ public function xdefinition(TextDocumentIdentifier $textDocument, Position $posi $packageName = $this->composerJson->name; } $descriptor = new SymbolDescriptor($def->fqn, new PackageDescriptor($packageName)); - return [new SymbolLocationInformation($descriptor, $def->symbolInformation->location)]; + $deferred->resolve([new SymbolLocationInformation($descriptor, $def->symbolInformation->location)]); }); + return $deferred->promise(); } } diff --git a/src/Server/Workspace.php b/src/Server/Workspace.php index fd93a9e1..e86d83a1 100644 --- a/src/Server/Workspace.php +++ b/src/Server/Workspace.php @@ -1,23 +1,17 @@ sourceIndex->isStaticComplete()) { - yield waitForEvent($this->sourceIndex, 'static-complete'); + $symbols = []; + foreach ($this->sourceIndex->getDefinitions() as $fqn => $definition) { + if ($query === '' || stripos($fqn, $query) !== false) { + $symbols[] = $definition->symbolInformation; } - $symbols = []; - foreach ($this->sourceIndex->getDefinitions() as $fqn => $definition) { - if ($query === '' || stripos($fqn, $query) !== false) { - $symbols[] = $definition->symbolInformation; - } - } - return $symbols; - }); + } + return new Success($symbols); } /** @@ -106,35 +94,35 @@ public function symbol(string $query): Promise */ public function didChangeWatchedFiles(array $changes) { - foreach ($changes as $change) { - if ($change->type === FileChangeType::DELETED) { - $this->client->textDocument->publishDiagnostics($change->uri, []); + Loop::defer(function () use ($changes) { + foreach ($changes as $change) { + if ($change->type === FileChangeType::DELETED) { + yield from $this->client->textDocument->publishDiagnostics($change->uri, []); + } } - } + }); } /** * The workspace references request is sent from the client to the server to locate project-wide references to a symbol given its description / metadata. * * @param SymbolDescriptor $query Partial metadata about the symbol that is being searched for. - * @param string[] $files An optional list of files to restrict the search to. - * @return ReferenceInformation[] + * @param string[] $files An optional list of files to restrict the search to. + * @return \Generator + * @throws \LanguageServer\ContentTooLargeException */ - public function xreferences($query, array $files = null): Promise + public function xreferences($query, array $files = null): \Generator { - // TODO: $files is unused in the coroutine - return coroutine(function () use ($query, $files) { + $deferred = new Deferred(); + Loop::defer(function () use ($deferred, $query, $files) { + // TODO: $files is unused in the coroutine if ($this->composerLock === null) { return []; } - // Wait until indexing finished - if (!$this->projectIndex->isComplete()) { - yield waitForEvent($this->projectIndex, 'complete'); - } /** Map from URI to array of referenced FQNs in dependencies */ $refs = []; // Get all references TO dependencies - $fqns = isset($query->fqsen) ? [$query->fqsen] : array_values($this->dependenciesIndex->getDefinitions()); + $fqns = isset($query->fqsen) ? [$query->fqsen] : array_values(yield from $this->dependenciesIndex->getDefinitions()); foreach ($fqns as $fqn) { foreach ($this->sourceIndex->getReferenceUris($fqn) as $uri) { if (!isset($refs[$uri])) { @@ -148,7 +136,7 @@ public function xreferences($query, array $files = null): Promise $refInfos = []; foreach ($refs as $uri => $fqns) { foreach ($fqns as $fqn) { - $doc = yield $this->documentLoader->getOrLoad($uri); + $doc = yield from $this->documentLoader->getOrLoad($uri); foreach ($doc->getReferenceNodesByFqn($fqn) as $node) { $refInfo = new ReferenceInformation; $refInfo->reference = LocationFactory::fromNode($node); @@ -157,8 +145,9 @@ public function xreferences($query, array $files = null): Promise } } } - return $refInfos; + $deferred->resolve($refInfos); }); + return $deferred->promise(); } /** diff --git a/src/SignatureHelpProvider.php b/src/SignatureHelpProvider.php index 66afd5fd..fb2601b6 100644 --- a/src/SignatureHelpProvider.php +++ b/src/SignatureHelpProvider.php @@ -1,16 +1,13 @@ */ - public function getSignatureHelp(PhpDocument $doc, Position $position): Promise + public function getSignatureHelp(PhpDocument $doc, Position $position): \Generator { - return coroutine(function () use ($doc, $position) { - // Find the node under the cursor - $node = $doc->getNodeAtPosition($position); + // Find the node under the cursor + $node = $doc->getNodeAtPosition($position); - // Find the definition of the item being called - list($def, $argumentExpressionList) = yield $this->getCallingInfo($node); + // Find the definition of the item being called + list($def, $argumentExpressionList) = yield from $this->getCallingInfo($node); - if (!$def || !$def->signatureInformation) { - return new SignatureHelp(); - } + if (!$def || !$def->signatureInformation) { + return new SignatureHelp(); + } - // Find the active parameter - $activeParam = $argumentExpressionList - ? $this->findActiveParameter($argumentExpressionList, $position, $doc) - : 0; + // Find the active parameter + $activeParam = $argumentExpressionList + ? $this->findActiveParameter($argumentExpressionList, $position, $doc) + : 0; - return new SignatureHelp([$def->signatureInformation], 0, $activeParam); - }); + return new SignatureHelp([$def->signatureInformation], 0, $activeParam); } /** @@ -73,83 +68,81 @@ public function getSignatureHelp(PhpDocument $doc, Position $position): Promise * * @param Node $node The node to find calling information from * - * @return Promise + * @return \Generator */ - private function getCallingInfo(Node $node) + private function getCallingInfo(Node $node): \Generator { - return coroutine(function () use ($node) { - $fqn = null; - $callingNode = null; - if ($node instanceof Node\DelimitedList\ArgumentExpressionList) { - // Cursor is already inside a ( - $argumentExpressionList = $node; - if ($node->parent instanceof Node\Expression\ObjectCreationExpression) { - // Constructing something - $callingNode = $node->parent->classTypeDesignator; - if (!$callingNode instanceof Node\QualifiedName) { - // We only support constructing from a QualifiedName - return null; - } - $fqn = $this->definitionResolver->resolveReferenceNodeToFqn($callingNode); - $fqn = "{$fqn}->__construct()"; - } else { - $callingNode = $node->parent->getFirstChildNode( - Node\Expression\MemberAccessExpression::class, - Node\Expression\ScopedPropertyAccessExpression::class, - Node\QualifiedName::class - ); - } - } elseif ($node instanceof Node\Expression\CallExpression) { - $argumentExpressionList = $node->getFirstChildNode(Node\DelimitedList\ArgumentExpressionList::class); - $callingNode = $node->getFirstChildNode( - Node\Expression\MemberAccessExpression::class, - Node\Expression\ScopedPropertyAccessExpression::class, - Node\QualifiedName::class - ); - } elseif ($node instanceof Node\Expression\ObjectCreationExpression) { - $argumentExpressionList = $node->getFirstChildNode(Node\DelimitedList\ArgumentExpressionList::class); - $callingNode = $node->classTypeDesignator; + $fqn = null; + $callingNode = null; + if ($node instanceof Node\DelimitedList\ArgumentExpressionList) { + // Cursor is already inside a ( + $argumentExpressionList = $node; + if ($node->parent instanceof Node\Expression\ObjectCreationExpression) { + // Constructing something + $callingNode = $node->parent->classTypeDesignator; if (!$callingNode instanceof Node\QualifiedName) { // We only support constructing from a QualifiedName return null; } - // Manually build the __construct fqn $fqn = $this->definitionResolver->resolveReferenceNodeToFqn($callingNode); $fqn = "{$fqn}->__construct()"; + } else { + $callingNode = $node->parent->getFirstChildNode( + Node\Expression\MemberAccessExpression::class, + Node\Expression\ScopedPropertyAccessExpression::class, + Node\QualifiedName::class + ); } - - if (!$callingNode) { + } elseif ($node instanceof Node\Expression\CallExpression) { + $argumentExpressionList = $node->getFirstChildNode(Node\DelimitedList\ArgumentExpressionList::class); + $callingNode = $node->getFirstChildNode( + Node\Expression\MemberAccessExpression::class, + Node\Expression\ScopedPropertyAccessExpression::class, + Node\QualifiedName::class + ); + } elseif ($node instanceof Node\Expression\ObjectCreationExpression) { + $argumentExpressionList = $node->getFirstChildNode(Node\DelimitedList\ArgumentExpressionList::class); + $callingNode = $node->classTypeDesignator; + if (!$callingNode instanceof Node\QualifiedName) { + // We only support constructing from a QualifiedName return null; } + // Manually build the __construct fqn + $fqn = $this->definitionResolver->resolveReferenceNodeToFqn($callingNode); + $fqn = "{$fqn}->__construct()"; + } - // Now find the definition of the call - $fqn = $fqn ?: DefinitionResolver::getDefinedFqn($callingNode); - while (true) { - if ($fqn) { - $def = $this->index->getDefinition($fqn); - } else { - $def = $this->definitionResolver->resolveReferenceNodeToDefinition($callingNode); - } - if ($def !== null || $this->index->isComplete()) { - break; - } - yield waitForEvent($this->index, 'definition-added'); - } + if (!$callingNode) { + return null; + } - if (!$def) { - return null; + // Now find the definition of the call + $fqn = $fqn ?: DefinitionResolver::getDefinedFqn($callingNode); + while (true) { + if ($fqn) { + $def = $this->index->getDefinition($fqn); + } else { + $def = $this->definitionResolver->resolveReferenceNodeToDefinition($callingNode); + } + if ($def !== null || $this->index->isComplete()) { + break; } + } + + if (!$def) { + return null; + } - return [$def, $argumentExpressionList]; - }); + yield new Delayed(0); + return [$def, $argumentExpressionList]; } /** * Given a position and arguments, finds the "active" argument at the position * * @param Node\DelimitedList\ArgumentExpressionList $argumentExpressionList The argument expression list - * @param Position $position The position to detect the active argument from - * @param PhpDocument $doc The document that contains the expression + * @param Position $position The position to detect the active argument from + * @param PhpDocument $doc The document that contains the expression * * @return int */ diff --git a/src/utils.php b/src/utils.php index af47a96e..50493c13 100644 --- a/src/utils.php +++ b/src/utils.php @@ -1,12 +1,12 @@ - */ -function timeout($seconds = 0): Promise -{ - $promise = new Promise; - Loop\setTimeout([$promise, 'fulfill'], $seconds); - return $promise; -} - -/** - * Returns a promise that is fulfilled once the passed event was triggered on the passed EventEmitter - * - * @param EmitterInterface $emitter - * @param string $event - * @return Promise - */ -function waitForEvent(EmitterInterface $emitter, string $event): Promise -{ - $p = new Promise; - $emitter->once($event, [$p, 'fulfill']); - return $p; -} - /** * Returns the part of $b that is not overlapped by $a * Example: @@ -125,7 +97,7 @@ function stripStringOverlap(string $a, string $b): string function sortUrisLevelOrder(&$uriList) { usort($uriList, function ($a, $b) { - return substr_count(Uri\parse($a)['path'], '/') - substr_count(Uri\parse($b)['path'], '/'); + return substr_count(parse($a)['path'], '/') - substr_count(parse($b)['path'], '/'); }); } @@ -133,13 +105,13 @@ function sortUrisLevelOrder(&$uriList) * Checks a document against the composer.json to see if it * is a vendored document * - * @param PhpDocument $document + * @param PhpDocument $document * @param \stdClass|null $composerJson * @return bool */ function isVendored(PhpDocument $document, \stdClass $composerJson = null): bool { - $path = Uri\parse($document->getUri())['path']; + $path = parse($document->getUri())['path']; $vendorDir = getVendorDir($composerJson); return strpos($path, "/$vendorDir/") !== false; } @@ -148,7 +120,7 @@ function isVendored(PhpDocument $document, \stdClass $composerJson = null): bool * Check a given URI against the composer.json to see if it * is a vendored URI * - * @param string $uri + * @param string $uri * @param \stdClass|null $composerJson * @return string|null */ diff --git a/tests/ClientHandlerTest.php b/tests/ClientHandlerTest.php index 19114d9e..352a599d 100644 --- a/tests/ClientHandlerTest.php +++ b/tests/ClientHandlerTest.php @@ -1,43 +1,48 @@ once('message', function (Message $msg) use ($reader) { - // Respond to request - Loop\setTimeout(function () use ($reader, $msg) { - $reader->write(new Message(new AdvancedJsonRpc\SuccessResponse($msg->body->id, 'pong'))); - }, 0); - }); - $handler->request('testMethod', ['ping'])->then(function ($result) { + Loop::run(function () { + $reader = new MockProtocolStream; + $writer = new MockProtocolStream; + $handler = new ClientHandler($reader, $writer); + $writer->addOneTimeListener('message', function (MessageEvent $messageEvent) use ($reader) { + $msg = $messageEvent->getMessage(); + // Respond to request + Loop::defer(function () use ($reader, $msg) { + yield from $reader->write(new Message(new AdvancedJsonRpc\SuccessResponse($msg->body->id, 'pong'))); + }); + }); + $result = yield from $handler->request('testMethod', ['ping']); $this->assertEquals('pong', $result); - })->wait(); - // No event listeners - $this->assertEquals([], $reader->listeners('message')); - $this->assertEquals([], $writer->listeners('message')); + // No event listeners + $this->assertEquals([], $reader->getListeners('message')); + $this->assertEquals([], $writer->getListeners('message')); + }); } public function testNotify() { - $reader = new MockProtocolStream; - $writer = new MockProtocolStream; - $handler = new ClientHandler($reader, $writer); - $handler->notify('testMethod', ['ping'])->wait(); - // No event listeners - $this->assertEquals([], $reader->listeners('message')); - $this->assertEquals([], $writer->listeners('message')); + Loop::run(function () { + $reader = new MockProtocolStream; + $writer = new MockProtocolStream; + $handler = new ClientHandler($reader, $writer); + yield from $handler->notify('testMethod', ['ping']); + // No event listeners + $this->assertEquals([], $reader->getListeners('message')); + $this->assertEquals([], $writer->getListeners('message')); + }); } } diff --git a/tests/LanguageServerTest.php b/tests/LanguageServerTest.php index 10ca3f90..6a9549b3 100644 --- a/tests/LanguageServerTest.php +++ b/tests/LanguageServerTest.php @@ -1,122 +1,132 @@ initialize(new ClientCapabilities, __DIR__, getmypid())->wait(); + Loop::run(function () { + $server = new LanguageServer(new MockProtocolStream, new MockProtocolStream); + $result = yield $server->initialize(new ClientCapabilities, __DIR__, getmypid()); - $serverCapabilities = new ServerCapabilities(); - $serverCapabilities->textDocumentSync = TextDocumentSyncKind::FULL; - $serverCapabilities->documentSymbolProvider = true; - $serverCapabilities->workspaceSymbolProvider = true; - $serverCapabilities->definitionProvider = true; - $serverCapabilities->referencesProvider = true; - $serverCapabilities->hoverProvider = true; - $serverCapabilities->completionProvider = new CompletionOptions; - $serverCapabilities->completionProvider->resolveProvider = false; - $serverCapabilities->completionProvider->triggerCharacters = ['$', '>']; - $serverCapabilities->signatureHelpProvider = new SignatureHelpOptions; - $serverCapabilities->signatureHelpProvider->triggerCharacters = ['(', ',']; - $serverCapabilities->xworkspaceReferencesProvider = true; - $serverCapabilities->xdefinitionProvider = true; - $serverCapabilities->xdependenciesProvider = true; + $serverCapabilities = new ServerCapabilities(); + $serverCapabilities->textDocumentSync = TextDocumentSyncKind::FULL; + $serverCapabilities->documentSymbolProvider = true; + $serverCapabilities->workspaceSymbolProvider = true; + $serverCapabilities->definitionProvider = true; + $serverCapabilities->referencesProvider = true; + $serverCapabilities->hoverProvider = true; + $serverCapabilities->completionProvider = new CompletionOptions; + $serverCapabilities->completionProvider->resolveProvider = false; + $serverCapabilities->completionProvider->triggerCharacters = ['$', '>']; + $serverCapabilities->signatureHelpProvider = new SignatureHelpOptions; + $serverCapabilities->signatureHelpProvider->triggerCharacters = ['(', ',']; + $serverCapabilities->xworkspaceReferencesProvider = true; + $serverCapabilities->xdefinitionProvider = true; + $serverCapabilities->xdependenciesProvider = true; - $this->assertEquals(new InitializeResult($serverCapabilities), $result); + $this->assertEquals(new InitializeResult($serverCapabilities), $result); + }); } public function testIndexingWithDirectFileAccess() { - $promise = new Promise; - $input = new MockProtocolStream; - $output = new MockProtocolStream; - $output->on('message', function (Message $msg) use ($promise) { - 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 (preg_match('/All \d+ PHP files parsed/', $msg->body->params->message)) { - $promise->fulfill(); - } - } + Loop::run(function () { + $deferred = new Deferred(); + $input = new MockProtocolStream; + $output = new MockProtocolStream; + $output->addListener('message', function (MessageEvent $messageEvent) use ($deferred) { + $msg = $messageEvent->getMessage(); + Loop::defer(function () use ($deferred, $msg) { + if ($msg->body->method === 'window/logMessage') { + if ($msg->body->params->type === MessageType::ERROR) { + $deferred->fail(new Exception($msg->body->params->message)); + } else if (preg_match('/All \d+ PHP files parsed/', $msg->body->params->message)) { + $deferred->resolve(true); + } + } + }); + }); + $server = new LanguageServer($input, $output); + $capabilities = new ClientCapabilities; + yield $server->initialize($capabilities, realpath(__DIR__ . '/../fixtures'), getmypid()); + $this->assertTrue(yield $deferred->promise()); }); - $server = new LanguageServer($input, $output); - $capabilities = new ClientCapabilities; - $server->initialize($capabilities, realpath(__DIR__ . '/../fixtures'), getmypid()); - $promise->wait(); } public function testIndexingWithFilesAndContentRequests() { - $promise = new Promise; - $filesCalled = false; - $contentCalled = false; - $rootPath = realpath(__DIR__ . '/../fixtures'); - $input = new MockProtocolStream; - $output = new MockProtocolStream; - $run = 1; - $output->on('message', function (Message $msg) use ($promise, $input, $rootPath, &$filesCalled, &$contentCalled, &$run) { - if ($msg->body->method === 'textDocument/xcontent') { - // Document content requested - $contentCalled = true; - $textDocumentItem = new TextDocumentItem; - $textDocumentItem->uri = $msg->body->params->textDocument->uri; - $textDocumentItem->version = 1; - $textDocumentItem->languageId = 'php'; - $textDocumentItem->text = file_get_contents($msg->body->params->textDocument->uri); - $input->write(new Message(new AdvancedJsonRpc\SuccessResponse($msg->body->id, $textDocumentItem))); - } else if ($msg->body->method === 'workspace/xfiles') { - // Files requested - $filesCalled = true; - $pattern = Path::makeAbsolute('**/*.php', $msg->body->params->base ?? $rootPath); - $files = []; - foreach (Glob::glob($pattern) as $path) { - $files[] = new TextDocumentIdentifier(pathToUri($path)); - } - $input->write(new Message(new AdvancedJsonRpc\SuccessResponse($msg->body->id, $files))); - } else if ($msg->body->method === 'window/logMessage') { - // Message logged - if ($msg->body->params->type === MessageType::ERROR) { - // Error happened during indexing, fail test - if ($promise->state === Promise::PENDING) { - $promise->reject(new Exception($msg->body->params->message)); + Loop::run(function () { + $deferred = new Deferred(); + $filesCalled = false; + $contentCalled = false; + $rootPath = realpath(__DIR__ . '/../fixtures'); + $input = new MockProtocolStream; + $output = new MockProtocolStream; + $run = 1; + $output->addListener('message', function (MessageEvent $messageEvent) use ($deferred, $input, $rootPath, &$filesCalled, &$contentCalled, &$run) { + $msg = $messageEvent->getMessage(); + Loop::defer(function () use ($msg, $deferred, $input, $rootPath, &$filesCalled, &$contentCalled, &$run) { + if ($msg->body->method === 'textDocument/xcontent') { + // Document content requested + $contentCalled = true; + $textDocumentItem = new TextDocumentItem; + $textDocumentItem->uri = $msg->body->params->textDocument->uri; + $textDocumentItem->version = 1; + $textDocumentItem->languageId = 'php'; + $textDocumentItem->text = file_get_contents($msg->body->params->textDocument->uri); + yield from $input->write(new Message(new AdvancedJsonRpc\SuccessResponse($msg->body->id, $textDocumentItem))); + } else if ($msg->body->method === 'workspace/xfiles') { + // Files requested + $filesCalled = true; + $pattern = Path::makeAbsolute('**/*.php', $msg->body->params->base ?? $rootPath); + $files = []; + foreach (Glob::glob($pattern) as $path) { + $files[] = new TextDocumentIdentifier(pathToUri($path)); + } + yield from $input->write(new Message(new AdvancedJsonRpc\SuccessResponse($msg->body->id, $files))); + } else if ($msg->body->method === 'window/logMessage') { + // Message logged + if ($msg->body->params->type === MessageType::ERROR) { + // Error happened during indexing, fail test + $deferred->fail(new Exception($msg->body->params->message)); + } else if (preg_match('/All \d+ PHP files parsed/', $msg->body->params->message)) { + $deferred->resolve(true); + } } - } else if (preg_match('/All \d+ PHP files parsed/', $msg->body->params->message)) { - $promise->fulfill(); - } - } + }); + }); + $server = new LanguageServer($input, $output); + $capabilities = new ClientCapabilities; + $capabilities->xfilesProvider = true; + $capabilities->xcontentProvider = true; + yield $server->initialize($capabilities, $rootPath, getmypid()); + yield $deferred->promise(); + $this->assertTrue($filesCalled); + $this->assertTrue($contentCalled); }); - $server = new LanguageServer($input, $output); - $capabilities = new ClientCapabilities; - $capabilities->xfilesProvider = true; - $capabilities->xcontentProvider = true; - $server->initialize($capabilities, $rootPath, getmypid()); - $promise->wait(); - $this->assertTrue($filesCalled); - $this->assertTrue($contentCalled); } } diff --git a/tests/MockProtocolStream.php b/tests/MockProtocolStream.php index 9287053d..0bf976fb 100644 --- a/tests/MockProtocolStream.php +++ b/tests/MockProtocolStream.php @@ -1,11 +1,15 @@ emit('message', [Message::parse((string)$msg)]); + Loop::defer(function () use ($msg) { + $this->emit(new MessageEvent('message', Message::parse((string)$msg))); }); - return Promise\resolve(null); + yield new Delayed(0); } } diff --git a/tests/PhpDocumentLoaderTest.php b/tests/PhpDocumentLoaderTest.php index 348f23fd..a26eeddd 100644 --- a/tests/PhpDocumentLoaderTest.php +++ b/tests/PhpDocumentLoaderTest.php @@ -1,8 +1,9 @@ loader->getOrLoad(pathToUri(__FILE__))->wait(); + Loop::run(function () { + $document = yield from $this->loader->getOrLoad(pathToUri(__FILE__)); - $this->assertNotNull($document); - $this->assertInstanceOf(PhpDocument::class, $document); + $this->assertNotNull($document); + $this->assertInstanceOf(PhpDocument::class, $document); + }); } public function testGetReturnsOpenedInstance() diff --git a/tests/ProtocolStreamReaderTest.php b/tests/ProtocolStreamReaderTest.php index a0dcad54..621cc33c 100644 --- a/tests/ProtocolStreamReaderTest.php +++ b/tests/ProtocolStreamReaderTest.php @@ -1,32 +1,49 @@ on('message', function (Message $message) use (&$msg) { - $msg = $message; + Loop::run(function () { + /** @var ResourceOutputStream $outputStream */ + /** @var ResourceInputStream $inputStream */ + list($outputStream, $inputStream) = $this->getStreamPair(); + + $reader = new ProtocolStreamReader($inputStream); + $deferred = new Deferred(); + $reader->addListener('message', function (MessageEvent $messageEvent) use (&$deferred) { + $deferred->resolve($messageEvent->getMessage()); + }); + + yield $outputStream->write((string)new Message(new RequestBody(1, 'aMethod', ['arg' => 'Hello World']))); + $msg = yield $deferred->promise(); + $this->assertNotNull($msg); + $this->assertInstanceOf(Message::class, $msg); + $this->assertInstanceOf(RequestBody::class, $msg->body); + $this->assertEquals(1, $msg->body->id); + $this->assertEquals('aMethod', $msg->body->method); + $this->assertEquals((object)['arg' => 'Hello World'], $msg->body->params); }); - $ret = fwrite($writeHandle, (string)new Message(new RequestBody(1, 'aMethod', ['arg' => 'Hello World']))); - Loop\tick(); - $this->assertNotNull($msg); - $this->assertInstanceOf(Message::class, $msg); - $this->assertInstanceOf(RequestBody::class, $msg->body); - $this->assertEquals(1, $msg->body->id); - $this->assertEquals('aMethod', $msg->body->method); - $this->assertEquals((object)['arg' => 'Hello World'], $msg->body->params); } } diff --git a/tests/ProtocolStreamWriterTest.php b/tests/ProtocolStreamWriterTest.php deleted file mode 100644 index 8ac94e74..00000000 --- a/tests/ProtocolStreamWriterTest.php +++ /dev/null @@ -1,35 +0,0 @@ - str_repeat('X', 100000)])); - $msgString = (string)$msg; - - $promise = $writer->write($msg); - - Loop\tick(); - - $promise->wait(); - - fclose($writeHandle); - - $this->assertEquals(strlen($msgString), filesize($tmpfile)); - } -} diff --git a/tests/Server/ServerTestCase.php b/tests/Server/ServerTestCase.php index 3331aefa..39462f2b 100644 --- a/tests/Server/ServerTestCase.php +++ b/tests/Server/ServerTestCase.php @@ -1,8 +1,9 @@ setComplete(); - - $definitionResolver = new DefinitionResolver($projectIndex); - $client = new LanguageClient(new MockProtocolStream, new MockProtocolStream); - $this->documentLoader = new PhpDocumentLoader(new FileSystemContentRetriever, $projectIndex, $definitionResolver); - $this->textDocument = new Server\TextDocument($this->documentLoader, $definitionResolver, $client, $projectIndex); - $this->workspace = new Server\Workspace($client, $projectIndex, $dependenciesIndex, $sourceIndex, null, $this->documentLoader); - - $globalSymbolsUri = pathToUri(realpath(__DIR__ . '/../../fixtures/global_symbols.php')); - $globalReferencesUri = pathToUri(realpath(__DIR__ . '/../../fixtures/global_references.php')); - $symbolsUri = pathToUri(realpath(__DIR__ . '/../../fixtures/symbols.php')); - $referencesUri = pathToUri(realpath(__DIR__ . '/../../fixtures/references.php')); - $useUri = pathToUri(realpath(__DIR__ . '/../../fixtures/use.php')); - - $this->documentLoader->load($symbolsUri)->wait(); - $this->documentLoader->load($referencesUri)->wait(); - $this->documentLoader->load($globalSymbolsUri)->wait(); - $this->documentLoader->load($globalReferencesUri)->wait(); - $this->documentLoader->load($useUri)->wait(); - - // @codingStandardsIgnoreStart - $this->definitionLocations = [ - - // Global - 'TEST_DEFINE_CONSTANT' => new Location($globalSymbolsUri, new Range(new Position(104, 0), new Position(104, 37))), - 'TEST_CONST' => new Location($globalSymbolsUri, new Range(new Position( 9, 6), new Position( 9, 22))), - 'TestClass' => new Location($globalSymbolsUri, new Range(new Position(20, 0), new Position(61, 1))), - 'ChildClass' => new Location($globalSymbolsUri, new Range(new Position(99, 0), new Position(99, 37))), - 'TestTrait' => new Location($globalSymbolsUri, new Range(new Position(63, 0), new Position(66, 1))), - 'TestInterface' => new Location($globalSymbolsUri, new Range(new Position(68, 0), new Position(71, 1))), - 'TestClass::TEST_CLASS_CONST' => new Location($globalSymbolsUri, new Range(new Position(27, 10), new Position(27, 32))), - 'TestClass::testProperty' => new Location($globalSymbolsUri, new Range(new Position(41, 11), new Position(41, 24))), - 'TestClass::staticTestProperty' => new Location($globalSymbolsUri, new Range(new Position(34, 18), new Position(34, 37))), - 'TestClass::staticTestMethod()' => new Location($globalSymbolsUri, new Range(new Position(46, 4), new Position(49, 5))), - 'TestClass::testMethod()' => new Location($globalSymbolsUri, new Range(new Position(57, 4), new Position(60, 5))), - 'test_function()' => new Location($globalSymbolsUri, new Range(new Position(78, 0), new Position(81, 1))), - 'UnusedClass' => new Location($globalSymbolsUri, new Range(new Position(111, 0), new Position(118, 1))), - 'UnusedClass::unusedProperty' => new Location($globalSymbolsUri, new Range(new Position(113,11), new Position(113, 26))), - 'UnusedClass::unusedMethod' => new Location($globalSymbolsUri, new Range(new Position(115, 4), new Position(117, 5))), - '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\\ChildClass' => new Location($symbolsUri, new Range(new Position(99, 0), new Position(99, 37))), - 'TestNamespace\\TestTrait' => new Location($symbolsUri, new Range(new Position(63, 0), new Position(66, 1))), - 'TestNamespace\\TestInterface' => new Location($symbolsUri, new Range(new Position(68, 0), new Position(71, 1))), - 'TestNamespace\\TestClass::TEST_CLASS_CONST' => new Location($symbolsUri, new Range(new Position(27, 10), new Position(27, 32))), - 'TestNamespace\\TestClass::testProperty' => new Location($symbolsUri, new Range(new Position(41, 11), new Position(41, 24))), - 'TestNamespace\\TestClass::staticTestProperty' => new Location($symbolsUri, new Range(new Position(34, 18), new Position(34, 37))), - 'TestNamespace\\TestClass::staticTestMethod()' => new Location($symbolsUri, new Range(new Position(46, 4), new Position(49, 5))), - 'TestNamespace\\TestClass::testMethod()' => new Location($symbolsUri, new Range(new Position(57, 4), new Position(60, 5))), - 'TestNamespace\\test_function()' => new Location($symbolsUri, new Range(new Position(78, 0), new Position(81, 1))), - 'TestNamespace\\whatever()' => new Location($referencesUri, new Range(new Position(21, 0), new Position(23, 1))), - 'TestNamespace\\Example' => new Location($symbolsUri, new Range(new Position(101, 0), new Position(104, 1))), - 'TestNamespace\\Example::__construct' => new Location($symbolsUri, new Range(new Position(102, 4), new Position(102, 36))), - 'TestNamespace\\Example::__destruct' => new Location($symbolsUri, new Range(new Position(103, 4), new Position(103, 35))), - 'TestNamespace\\InnerNamespace' => new Location($symbolsUri, new Range(new Position(106, 0), new Position(106, 39))), - 'TestNamespace\\InnerNamespace\\InnerClass' => new Location($symbolsUri, new Range(new Position(108, 0), new Position(109, 1))), - ]; - - $this->referenceLocations = [ - - // Namespaced - 'TestNamespace' => [ - 0 => new Location($referencesUri, new Range(new Position(31, 13), new Position(31, 40))), // use function TestNamespace\test_function; - 1 => new Location($useUri, new Range(new Position( 4, 4), new Position( 4, 27))), // use TestNamespace\TestClass; - 2 => new Location($useUri, new Range(new Position( 5, 4), new Position( 5, 18))) // use TestNamespace\{TestTrait, TestInterface}; - ], - 'TestNamespace\\TEST_CONST' => [ - 0 => new Location($referencesUri, new Range(new Position(29, 5), new Position(29, 15))) - ], - 'TestNamespace\\TestClass' => [ - 0 => new Location($symbolsUri, new Range(new Position(48, 13), new Position(48, 17))), // echo self::TEST_CLASS_CONST; - 1 => new Location($symbolsUri , new Range(new Position(99, 25), new Position(99, 34))), // class ChildClass extends TestClass {} - 2 => new Location($referencesUri, new Range(new Position( 4, 11), new Position( 4, 20))), // $obj = new TestClass(); - 3 => new Location($referencesUri, new Range(new Position( 7, 0), new Position( 7, 9))), // TestClass::staticTestMethod(); - 4 => new Location($referencesUri, new Range(new Position( 8, 5), new Position( 8, 14))), // echo TestClass::$staticTestProperty; - 5 => new Location($referencesUri, new Range(new Position( 9, 5), new Position( 9, 14))), // TestClass::TEST_CLASS_CONST; - 6 => new Location($referencesUri, new Range(new Position(21, 18), new Position(21, 27))), // function whatever(TestClass $param) - 7 => new Location($referencesUri, new Range(new Position(21, 37), new Position(21, 46))), // function whatever(TestClass $param): TestClass - 8 => new Location($referencesUri, new Range(new Position(39, 0), new Position(39, 9))), // TestClass::$staticTestProperty[123]->testProperty; - 9 => new Location($useUri, new Range(new Position( 4, 4), new Position( 4, 27))), // use TestNamespace\TestClass; - ], - 'TestNamespace\\TestChild' => [ - 0 => new Location($referencesUri, new Range(new Position(42, 5), new Position(42, 25))), // echo $child->testProperty; - ], - 'TestNamespace\\TestInterface' => [ - 0 => new Location($symbolsUri, new Range(new Position(20, 27), new Position(20, 40))), // class TestClass implements TestInterface - 1 => new Location($symbolsUri, new Range(new Position(57, 48), new Position(57, 61))), // public function testMethod($testParameter): TestInterface - 2 => new Location($referencesUri, new Range(new Position(33, 20), new Position(33, 33))) // if ($abc instanceof TestInterface) - ], - 'TestNamespace\\TestClass::TEST_CLASS_CONST' => [ - 0 => new Location($symbolsUri, new Range(new Position(48, 13), new Position(48, 35))), // echo self::TEST_CLASS_CONSTANT - 1 => new Location($referencesUri, new Range(new Position( 9, 5), new Position( 9, 32))) - ], - 'TestNamespace\\TestClass::testProperty' => [ - 0 => new Location($symbolsUri, new Range(new Position(59, 8), new Position(59, 27))), // $this->testProperty = $testParameter; - 1 => new Location($referencesUri, new Range(new Position( 6, 5), new Position( 6, 23))), // echo $obj->testProperty; - 2 => new Location($referencesUri, new Range(new Position(38, 0), new Position(38, 18))), // $obj->testProperty->testMethod(); - 3 => new Location($referencesUri, new Range(new Position(39, 0), new Position(39, 49))) // TestClass::$staticTestProperty[123]->testProperty; - ], - 'TestNamespace\\TestClass::staticTestProperty' => [ - 0 => new Location($referencesUri, new Range(new Position( 8, 16), new Position( 8, 35))), // echo TestClass::$staticTestProperty; - 1 => new Location($referencesUri, new Range(new Position(39, 11), new Position(39, 30))) // TestClass::$staticTestProperty[123]->testProperty; - ], - 'TestNamespace\\TestClass::staticTestMethod()' => [ - 0 => new Location($referencesUri, new Range(new Position( 7, 0), new Position( 7, 27))) - ], - 'TestNamespace\\TestClass::testMethod()' => [ - 0 => new Location($referencesUri, new Range(new Position( 5, 0), new Position( 5, 16))), // $obj->testMethod(); - 1 => new Location($referencesUri, new Range(new Position(38, 0), new Position(38, 30))), // $obj->testProperty->testMethod(); - 2 => new Location($referencesUri, new Range(new Position(42, 5), new Position(42, 23))) // $child->testMethod(); - ], - 'TestNamespace\\test_function()' => [ - 0 => new Location($referencesUri, new Range(new Position(10, 0), new Position(10, 13))), - 1 => new Location($referencesUri, new Range(new Position(31, 13), new Position(31, 40))) - ], - - // Global - 'TEST_DEFINE_CONSTANT' => [ - 0 => new Location($globalSymbolsUri, new Range(new Position(106, 6), new Position(106, 26))) - ], - 'TEST_CONST' => [ - 0 => new Location($referencesUri, new Range(new Position(29, 5), new Position(29, 15))), - 1 => new Location($globalReferencesUri, new Range(new Position(29, 5), new Position(29, 15))) - ], - 'TestClass' => [ - 0 => new Location($globalSymbolsUri, new Range(new Position(48, 13), new Position(48, 17))), // echo self::TEST_CLASS_CONST; - 1 => new Location($globalSymbolsUri, new Range(new Position(99, 25), new Position(99, 34))), // class ChildClass extends TestClass {} - 2 => new Location($globalReferencesUri, new Range(new Position( 4, 11), new Position( 4, 20))), // $obj = new TestClass(); - 3 => new Location($globalReferencesUri, new Range(new Position( 7, 0), new Position( 7, 9))), // TestClass::staticTestMethod(); - 4 => new Location($globalReferencesUri, new Range(new Position( 8, 5), new Position( 8, 14))), // echo TestClass::$staticTestProperty; - 5 => new Location($globalReferencesUri, new Range(new Position( 9, 5), new Position( 9, 14))), // TestClass::TEST_CLASS_CONST; - 6 => new Location($globalReferencesUri, new Range(new Position(21, 18), new Position(21, 27))), // function whatever(TestClass $param) - 7 => new Location($globalReferencesUri, new Range(new Position(21, 37), new Position(21, 46))), // function whatever(TestClass $param): TestClass - 8 => new Location($globalReferencesUri, new Range(new Position(39, 0), new Position(39, 9))), // TestClass::$staticTestProperty[123]->testProperty; - ], - 'TestChild' => [ - 0 => new Location($globalReferencesUri, new Range(new Position(42, 5), new Position(42, 25))), // echo $child->testProperty; - ], - 'TestInterface' => [ - 0 => new Location($globalSymbolsUri, new Range(new Position(20, 27), new Position(20, 40))), // class TestClass implements TestInterface - 1 => new Location($globalSymbolsUri, new Range(new Position(57, 49), new Position(57, 61))), // public function testMethod($testParameter) : TestInterface - 2 => new Location($globalReferencesUri, new Range(new Position(33, 20), new Position(33, 33))) // if ($abc instanceof TestInterface) - ], - 'TestClass::TEST_CLASS_CONST' => [ - 0 => new Location($globalSymbolsUri, new Range(new Position(48, 13), new Position(48, 35))), // echo self::TEST_CLASS_CONSTANT - 1 => new Location($globalReferencesUri, new Range(new Position( 9, 5), new Position( 9, 32))) - ], - 'TestClass::testProperty' => [ - 0 => new Location($globalSymbolsUri, new Range(new Position(59, 8), new Position(59, 27))), // $this->testProperty = $testParameter; - 1 => new Location($globalReferencesUri, new Range(new Position( 6, 5), new Position( 6, 23))), // echo $obj->testProperty; - 2 => new Location($globalReferencesUri, new Range(new Position(38, 0), new Position(38, 18))), // $obj->testProperty->testMethod(); - 3 => new Location($globalReferencesUri, new Range(new Position(39, 0), new Position(39, 49))) // TestClass::$staticTestProperty[123]->testProperty; - ], - 'TestClass::staticTestProperty' => [ - 0 => new Location($globalReferencesUri, new Range(new Position( 8, 16), new Position( 8, 35))), // echo TestClass::$staticTestProperty; - 1 => new Location($globalReferencesUri, new Range(new Position(39, 11), new Position(39, 30))) // TestClass::$staticTestProperty[123]->testProperty; - ], - 'TestClass::staticTestMethod()' => [ - 0 => new Location($globalReferencesUri, new Range(new Position( 7, 0), new Position( 7, 27))) - ], - 'TestClass::testMethod()' => [ - 0 => new Location($globalReferencesUri, new Range(new Position( 5, 0), new Position( 5, 16))), // $obj->testMethod(); - 1 => new Location($globalReferencesUri, new Range(new Position(38, 0), new Position(38, 30))), // $obj->testProperty->testMethod(); - 2 => new Location($globalReferencesUri, new Range(new Position(42, 5), new Position(42, 23))) // $child->testMethod(); - ], - 'test_function()' => [ - 0 => new Location($globalReferencesUri, new Range(new Position(10, 0), new Position(10, 13))), - 1 => new Location($globalReferencesUri, new Range(new Position(31, 13), new Position(31, 40))) - ] - ]; - // @codingStandardsIgnoreEnd + Loop::run(function () { + $sourceIndex = new Index; + $dependenciesIndex = new DependenciesIndex; + $projectIndex = new ProjectIndex($sourceIndex, $dependenciesIndex); + $projectIndex->setComplete(); + + $definitionResolver = new DefinitionResolver($projectIndex); + $client = new LanguageClient(new MockProtocolStream, new MockProtocolStream); + $this->documentLoader = new PhpDocumentLoader(new FileSystemContentRetriever, $projectIndex, $definitionResolver); + $this->textDocument = new Server\TextDocument($this->documentLoader, $definitionResolver, $client, $projectIndex); + $this->workspace = new Server\Workspace($client, $projectIndex, $dependenciesIndex, $sourceIndex, null, $this->documentLoader); + + $globalSymbolsUri = pathToUri(realpath(__DIR__ . '/../../fixtures/global_symbols.php')); + $globalReferencesUri = pathToUri(realpath(__DIR__ . '/../../fixtures/global_references.php')); + $symbolsUri = pathToUri(realpath(__DIR__ . '/../../fixtures/symbols.php')); + $referencesUri = pathToUri(realpath(__DIR__ . '/../../fixtures/references.php')); + $useUri = pathToUri(realpath(__DIR__ . '/../../fixtures/use.php')); + + yield from $this->documentLoader->load($symbolsUri); + yield from $this->documentLoader->load($referencesUri); + yield from $this->documentLoader->load($globalSymbolsUri); + yield from $this->documentLoader->load($globalReferencesUri); + yield from $this->documentLoader->load($useUri); + + // @codingStandardsIgnoreStart + $this->definitionLocations = [ + + // Global + 'TEST_DEFINE_CONSTANT' => new Location($globalSymbolsUri, new Range(new Position(104, 0), new Position(104, 37))), + 'TEST_CONST' => new Location($globalSymbolsUri, new Range(new Position(9, 6), new Position(9, 22))), + 'TestClass' => new Location($globalSymbolsUri, new Range(new Position(20, 0), new Position(61, 1))), + 'ChildClass' => new Location($globalSymbolsUri, new Range(new Position(99, 0), new Position(99, 37))), + 'TestTrait' => new Location($globalSymbolsUri, new Range(new Position(63, 0), new Position(66, 1))), + 'TestInterface' => new Location($globalSymbolsUri, new Range(new Position(68, 0), new Position(71, 1))), + 'TestClass::TEST_CLASS_CONST' => new Location($globalSymbolsUri, new Range(new Position(27, 10), new Position(27, 32))), + 'TestClass::testProperty' => new Location($globalSymbolsUri, new Range(new Position(41, 11), new Position(41, 24))), + 'TestClass::staticTestProperty' => new Location($globalSymbolsUri, new Range(new Position(34, 18), new Position(34, 37))), + 'TestClass::staticTestMethod()' => new Location($globalSymbolsUri, new Range(new Position(46, 4), new Position(49, 5))), + 'TestClass::testMethod()' => new Location($globalSymbolsUri, new Range(new Position(57, 4), new Position(60, 5))), + 'test_function()' => new Location($globalSymbolsUri, new Range(new Position(78, 0), new Position(81, 1))), + 'UnusedClass' => new Location($globalSymbolsUri, new Range(new Position(111, 0), new Position(118, 1))), + 'UnusedClass::unusedProperty' => new Location($globalSymbolsUri, new Range(new Position(113, 11), new Position(113, 26))), + 'UnusedClass::unusedMethod' => new Location($globalSymbolsUri, new Range(new Position(115, 4), new Position(117, 5))), + '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\\ChildClass' => new Location($symbolsUri, new Range(new Position(99, 0), new Position(99, 37))), + 'TestNamespace\\TestTrait' => new Location($symbolsUri, new Range(new Position(63, 0), new Position(66, 1))), + 'TestNamespace\\TestInterface' => new Location($symbolsUri, new Range(new Position(68, 0), new Position(71, 1))), + 'TestNamespace\\TestClass::TEST_CLASS_CONST' => new Location($symbolsUri, new Range(new Position(27, 10), new Position(27, 32))), + 'TestNamespace\\TestClass::testProperty' => new Location($symbolsUri, new Range(new Position(41, 11), new Position(41, 24))), + 'TestNamespace\\TestClass::staticTestProperty' => new Location($symbolsUri, new Range(new Position(34, 18), new Position(34, 37))), + 'TestNamespace\\TestClass::staticTestMethod()' => new Location($symbolsUri, new Range(new Position(46, 4), new Position(49, 5))), + 'TestNamespace\\TestClass::testMethod()' => new Location($symbolsUri, new Range(new Position(57, 4), new Position(60, 5))), + 'TestNamespace\\test_function()' => new Location($symbolsUri, new Range(new Position(78, 0), new Position(81, 1))), + 'TestNamespace\\whatever()' => new Location($referencesUri, new Range(new Position(21, 0), new Position(23, 1))), + 'TestNamespace\\Example' => new Location($symbolsUri, new Range(new Position(101, 0), new Position(104, 1))), + 'TestNamespace\\Example::__construct' => new Location($symbolsUri, new Range(new Position(102, 4), new Position(102, 36))), + 'TestNamespace\\Example::__destruct' => new Location($symbolsUri, new Range(new Position(103, 4), new Position(103, 35))), + 'TestNamespace\\InnerNamespace' => new Location($symbolsUri, new Range(new Position(106, 0), new Position(106, 39))), + 'TestNamespace\\InnerNamespace\\InnerClass' => new Location($symbolsUri, new Range(new Position(108, 0), new Position(109, 1))), + ]; + + $this->referenceLocations = [ + + // Namespaced + 'TestNamespace' => [ + 0 => new Location($referencesUri, new Range(new Position(31, 13), new Position(31, 40))), // use function TestNamespace\test_function; + 1 => new Location($useUri, new Range(new Position(4, 4), new Position(4, 27))), // use TestNamespace\TestClass; + 2 => new Location($useUri, new Range(new Position(5, 4), new Position(5, 18))) // use TestNamespace\{TestTrait, TestInterface}; + ], + 'TestNamespace\\TEST_CONST' => [ + 0 => new Location($referencesUri, new Range(new Position(29, 5), new Position(29, 15))) + ], + 'TestNamespace\\TestClass' => [ + 0 => new Location($symbolsUri, new Range(new Position(48, 13), new Position(48, 17))), // echo self::TEST_CLASS_CONST; + 1 => new Location($symbolsUri, new Range(new Position(99, 25), new Position(99, 34))), // class ChildClass extends TestClass {} + 2 => new Location($referencesUri, new Range(new Position(4, 11), new Position(4, 20))), // $obj = new TestClass(); + 3 => new Location($referencesUri, new Range(new Position(7, 0), new Position(7, 9))), // TestClass::staticTestMethod(); + 4 => new Location($referencesUri, new Range(new Position(8, 5), new Position(8, 14))), // echo TestClass::$staticTestProperty; + 5 => new Location($referencesUri, new Range(new Position(9, 5), new Position(9, 14))), // TestClass::TEST_CLASS_CONST; + 6 => new Location($referencesUri, new Range(new Position(21, 18), new Position(21, 27))), // function whatever(TestClass $param) + 7 => new Location($referencesUri, new Range(new Position(21, 37), new Position(21, 46))), // function whatever(TestClass $param): TestClass + 8 => new Location($referencesUri, new Range(new Position(39, 0), new Position(39, 9))), // TestClass::$staticTestProperty[123]->testProperty; + 9 => new Location($useUri, new Range(new Position(4, 4), new Position(4, 27))), // use TestNamespace\TestClass; + ], + 'TestNamespace\\TestChild' => [ + 0 => new Location($referencesUri, new Range(new Position(42, 5), new Position(42, 25))), // echo $child->testProperty; + ], + 'TestNamespace\\TestInterface' => [ + 0 => new Location($symbolsUri, new Range(new Position(20, 27), new Position(20, 40))), // class TestClass implements TestInterface + 1 => new Location($symbolsUri, new Range(new Position(57, 48), new Position(57, 61))), // public function testMethod($testParameter): TestInterface + 2 => new Location($referencesUri, new Range(new Position(33, 20), new Position(33, 33))) // if ($abc instanceof TestInterface) + ], + 'TestNamespace\\TestClass::TEST_CLASS_CONST' => [ + 0 => new Location($symbolsUri, new Range(new Position(48, 13), new Position(48, 35))), // echo self::TEST_CLASS_CONSTANT + 1 => new Location($referencesUri, new Range(new Position(9, 5), new Position(9, 32))) + ], + 'TestNamespace\\TestClass::testProperty' => [ + 0 => new Location($symbolsUri, new Range(new Position(59, 8), new Position(59, 27))), // $this->testProperty = $testParameter; + 1 => new Location($referencesUri, new Range(new Position(6, 5), new Position(6, 23))), // echo $obj->testProperty; + 2 => new Location($referencesUri, new Range(new Position(38, 0), new Position(38, 18))), // $obj->testProperty->testMethod(); + 3 => new Location($referencesUri, new Range(new Position(39, 0), new Position(39, 49))) // TestClass::$staticTestProperty[123]->testProperty; + ], + 'TestNamespace\\TestClass::staticTestProperty' => [ + 0 => new Location($referencesUri, new Range(new Position(8, 16), new Position(8, 35))), // echo TestClass::$staticTestProperty; + 1 => new Location($referencesUri, new Range(new Position(39, 11), new Position(39, 30))) // TestClass::$staticTestProperty[123]->testProperty; + ], + 'TestNamespace\\TestClass::staticTestMethod()' => [ + 0 => new Location($referencesUri, new Range(new Position(7, 0), new Position(7, 27))) + ], + 'TestNamespace\\TestClass::testMethod()' => [ + 0 => new Location($referencesUri, new Range(new Position(5, 0), new Position(5, 16))), // $obj->testMethod(); + 1 => new Location($referencesUri, new Range(new Position(38, 0), new Position(38, 30))), // $obj->testProperty->testMethod(); + 2 => new Location($referencesUri, new Range(new Position(42, 5), new Position(42, 23))) // $child->testMethod(); + ], + 'TestNamespace\\test_function()' => [ + 0 => new Location($referencesUri, new Range(new Position(10, 0), new Position(10, 13))), + 1 => new Location($referencesUri, new Range(new Position(31, 13), new Position(31, 40))) + ], + + // Global + 'TEST_DEFINE_CONSTANT' => [ + 0 => new Location($globalSymbolsUri, new Range(new Position(106, 6), new Position(106, 26))) + ], + 'TEST_CONST' => [ + 0 => new Location($referencesUri, new Range(new Position(29, 5), new Position(29, 15))), + 1 => new Location($globalReferencesUri, new Range(new Position(29, 5), new Position(29, 15))) + ], + 'TestClass' => [ + 0 => new Location($globalSymbolsUri, new Range(new Position(48, 13), new Position(48, 17))), // echo self::TEST_CLASS_CONST; + 1 => new Location($globalSymbolsUri, new Range(new Position(99, 25), new Position(99, 34))), // class ChildClass extends TestClass {} + 2 => new Location($globalReferencesUri, new Range(new Position(4, 11), new Position(4, 20))), // $obj = new TestClass(); + 3 => new Location($globalReferencesUri, new Range(new Position(7, 0), new Position(7, 9))), // TestClass::staticTestMethod(); + 4 => new Location($globalReferencesUri, new Range(new Position(8, 5), new Position(8, 14))), // echo TestClass::$staticTestProperty; + 5 => new Location($globalReferencesUri, new Range(new Position(9, 5), new Position(9, 14))), // TestClass::TEST_CLASS_CONST; + 6 => new Location($globalReferencesUri, new Range(new Position(21, 18), new Position(21, 27))), // function whatever(TestClass $param) + 7 => new Location($globalReferencesUri, new Range(new Position(21, 37), new Position(21, 46))), // function whatever(TestClass $param): TestClass + 8 => new Location($globalReferencesUri, new Range(new Position(39, 0), new Position(39, 9))), // TestClass::$staticTestProperty[123]->testProperty; + ], + 'TestChild' => [ + 0 => new Location($globalReferencesUri, new Range(new Position(42, 5), new Position(42, 25))), // echo $child->testProperty; + ], + 'TestInterface' => [ + 0 => new Location($globalSymbolsUri, new Range(new Position(20, 27), new Position(20, 40))), // class TestClass implements TestInterface + 1 => new Location($globalSymbolsUri, new Range(new Position(57, 49), new Position(57, 61))), // public function testMethod($testParameter) : TestInterface + 2 => new Location($globalReferencesUri, new Range(new Position(33, 20), new Position(33, 33))) // if ($abc instanceof TestInterface) + ], + 'TestClass::TEST_CLASS_CONST' => [ + 0 => new Location($globalSymbolsUri, new Range(new Position(48, 13), new Position(48, 35))), // echo self::TEST_CLASS_CONSTANT + 1 => new Location($globalReferencesUri, new Range(new Position(9, 5), new Position(9, 32))) + ], + 'TestClass::testProperty' => [ + 0 => new Location($globalSymbolsUri, new Range(new Position(59, 8), new Position(59, 27))), // $this->testProperty = $testParameter; + 1 => new Location($globalReferencesUri, new Range(new Position(6, 5), new Position(6, 23))), // echo $obj->testProperty; + 2 => new Location($globalReferencesUri, new Range(new Position(38, 0), new Position(38, 18))), // $obj->testProperty->testMethod(); + 3 => new Location($globalReferencesUri, new Range(new Position(39, 0), new Position(39, 49))) // TestClass::$staticTestProperty[123]->testProperty; + ], + 'TestClass::staticTestProperty' => [ + 0 => new Location($globalReferencesUri, new Range(new Position(8, 16), new Position(8, 35))), // echo TestClass::$staticTestProperty; + 1 => new Location($globalReferencesUri, new Range(new Position(39, 11), new Position(39, 30))) // TestClass::$staticTestProperty[123]->testProperty; + ], + 'TestClass::staticTestMethod()' => [ + 0 => new Location($globalReferencesUri, new Range(new Position(7, 0), new Position(7, 27))) + ], + 'TestClass::testMethod()' => [ + 0 => new Location($globalReferencesUri, new Range(new Position(5, 0), new Position(5, 16))), // $obj->testMethod(); + 1 => new Location($globalReferencesUri, new Range(new Position(38, 0), new Position(38, 30))), // $obj->testProperty->testMethod(); + 2 => new Location($globalReferencesUri, new Range(new Position(42, 5), new Position(42, 23))) // $child->testMethod(); + ], + 'test_function()' => [ + 0 => new Location($globalReferencesUri, new Range(new Position(10, 0), new Position(10, 13))), + 1 => new Location($globalReferencesUri, new Range(new Position(31, 13), new Position(31, 40))) + ] + ]; + // @codingStandardsIgnoreEnd + }); } protected function getDefinitionLocation(string $fqn): Location diff --git a/tests/Server/TextDocument/CompletionTest.php b/tests/Server/TextDocument/CompletionTest.php index 516d3b40..d203ea93 100644 --- a/tests/Server/TextDocument/CompletionTest.php +++ b/tests/Server/TextDocument/CompletionTest.php @@ -1,8 +1,9 @@ loader = new PhpDocumentLoader($contentRetriever, $projectIndex, $definitionResolver); - $this->loader->load(pathToUri(__DIR__ . '/../../../fixtures/global_symbols.php'))->wait(); - $this->loader->load(pathToUri(__DIR__ . '/../../../fixtures/symbols.php'))->wait(); - $this->textDocument = new Server\TextDocument($this->loader, $definitionResolver, $client, $projectIndex); + Loop::run(function () { + $client = new LanguageClient(new MockProtocolStream, new MockProtocolStream); + $projectIndex = new ProjectIndex(new Index, new DependenciesIndex); + $definitionResolver = new DefinitionResolver($projectIndex); + $contentRetriever = new FileSystemContentRetriever; + $this->loader = new PhpDocumentLoader($contentRetriever, $projectIndex, $definitionResolver); + yield from $this->loader->load(pathToUri(__DIR__ . '/../../../fixtures/global_symbols.php')); + yield from $this->loader->load(pathToUri(__DIR__ . '/../../../fixtures/symbols.php')); + $this->textDocument = new Server\TextDocument($this->loader, $definitionResolver, $client, $projectIndex); + }); } /** @@ -52,26 +55,28 @@ public function setUp() */ public function testPropertyAndMethodWithPrefix() { - $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/property_with_prefix.php'); - $this->loader->open($completionUri, file_get_contents($completionUri)); - $items = $this->textDocument->completion( - new TextDocumentIdentifier($completionUri), - new Position(3, 7) - )->wait(); - $this->assertCompletionsListSubset(new CompletionList([ - new CompletionItem( - 'testProperty', - CompletionItemKind::PROPERTY, - '\TestClass', // Type of the property - 'Reprehenderit magna velit mollit ipsum do.' - ), - new CompletionItem( - 'testMethod', - CompletionItemKind::METHOD, - '\TestClass', // Return type of the method - 'Non culpa nostrud mollit esse sunt laboris in irure ullamco cupidatat amet.' - ) - ], true), $items); + Loop::run(function () { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/property_with_prefix.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = yield $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(3, 7) + ); + $this->assertCompletionsListSubset(new CompletionList([ + new CompletionItem( + 'testProperty', + CompletionItemKind::PROPERTY, + '\TestClass', // Type of the property + 'Reprehenderit magna velit mollit ipsum do.' + ), + new CompletionItem( + 'testMethod', + CompletionItemKind::METHOD, + '\TestClass', // Return type of the method + 'Non culpa nostrud mollit esse sunt laboris in irure ullamco cupidatat amet.' + ) + ], true), $items); + }); } /** @@ -79,23 +84,25 @@ public function testPropertyAndMethodWithPrefix() */ public function testGlobalFunctionInsideNamespaceAndClass() { - $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/inside_namespace_and_method.php'); - $this->loader->open($completionUri, file_get_contents($completionUri)); - $items = $this->textDocument->completion( - new TextDocumentIdentifier($completionUri), - new Position(8, 11) - )->wait(); - $this->assertCompletionsListSubset(new CompletionList([ - new CompletionItem( - 'test_function', - CompletionItemKind::FUNCTION, - 'void', // Return type - 'Officia aliquip adipisicing et nulla et laboris dolore labore.', - null, - null, - '\test_function' - ) - ], true), $items); + Loop::run(function () { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/inside_namespace_and_method.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = yield $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(8, 11) + ); + $this->assertCompletionsListSubset(new CompletionList([ + new CompletionItem( + 'test_function', + CompletionItemKind::FUNCTION, + 'void', // Return type + 'Officia aliquip adipisicing et nulla et laboris dolore labore.', + null, + null, + '\test_function' + ) + ], true), $items); + }); } /** @@ -103,26 +110,28 @@ public function testGlobalFunctionInsideNamespaceAndClass() */ public function testPropertyAndMethodWithoutPrefix() { - $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/property.php'); - $this->loader->open($completionUri, file_get_contents($completionUri)); - $items = $this->textDocument->completion( - new TextDocumentIdentifier($completionUri), - new Position(3, 6) - )->wait(); - $this->assertCompletionsListSubset(new CompletionList([ - new CompletionItem( - 'testProperty', - CompletionItemKind::PROPERTY, - '\TestClass', // Type of the property - 'Reprehenderit magna velit mollit ipsum do.' - ), - new CompletionItem( - 'testMethod', - CompletionItemKind::METHOD, - '\TestClass', // Return type of the method - 'Non culpa nostrud mollit esse sunt laboris in irure ullamco cupidatat amet.' - ) - ], true), $items); + Loop::run(function () { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/property.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = yield $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(3, 6) + ); + $this->assertCompletionsListSubset(new CompletionList([ + new CompletionItem( + 'testProperty', + CompletionItemKind::PROPERTY, + '\TestClass', // Type of the property + 'Reprehenderit magna velit mollit ipsum do.' + ), + new CompletionItem( + 'testMethod', + CompletionItemKind::METHOD, + '\TestClass', // Return type of the method + 'Non culpa nostrud mollit esse sunt laboris in irure ullamco cupidatat amet.' + ) + ], true), $items); + }); } /** @@ -130,34 +139,36 @@ public function testPropertyAndMethodWithoutPrefix() */ public function testVariable() { - $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/variable.php'); - $this->loader->open($completionUri, file_get_contents($completionUri)); - $items = $this->textDocument->completion( - new TextDocumentIdentifier($completionUri), - new Position(8, 5) - )->wait(); - $this->assertCompletionsListSubset(new CompletionList([ - new CompletionItem( - '$var', - CompletionItemKind::VARIABLE, - 'int', - null, - null, - null, - null, - new TextEdit(new Range(new Position(8, 5), new Position(8, 5)), 'var') - ), - new CompletionItem( - '$param', - CompletionItemKind::VARIABLE, - 'string|null', - 'A parameter', - null, - null, - null, - new TextEdit(new Range(new Position(8, 5), new Position(8, 5)), 'param') - ) - ], true), $items); + Loop::run(function () { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/variable.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = yield $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(8, 5) + ); + $this->assertCompletionsListSubset(new CompletionList([ + new CompletionItem( + '$var', + CompletionItemKind::VARIABLE, + 'int', + null, + null, + null, + null, + new TextEdit(new Range(new Position(8, 5), new Position(8, 5)), 'var') + ), + new CompletionItem( + '$param', + CompletionItemKind::VARIABLE, + 'string|null', + 'A parameter', + null, + null, + null, + new TextEdit(new Range(new Position(8, 5), new Position(8, 5)), 'param') + ) + ], true), $items); + }); } /** @@ -165,24 +176,26 @@ public function testVariable() */ public function testVariableWithPrefix() { - $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/variable_with_prefix.php'); - $this->loader->open($completionUri, file_get_contents($completionUri)); - $items = $this->textDocument->completion( - new TextDocumentIdentifier($completionUri), - new Position(8, 6) - )->wait(); - $this->assertCompletionsListSubset(new CompletionList([ - new CompletionItem( - '$param', - CompletionItemKind::VARIABLE, - 'string|null', - 'A parameter', - null, - null, - null, - new TextEdit(new Range(new Position(8, 6), new Position(8, 6)), 'aram') - ) - ], true), $items); + Loop::run(function () { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/variable_with_prefix.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = yield $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(8, 6) + ); + $this->assertCompletionsListSubset(new CompletionList([ + new CompletionItem( + '$param', + CompletionItemKind::VARIABLE, + 'string|null', + 'A parameter', + null, + null, + null, + new TextEdit(new Range(new Position(8, 6), new Position(8, 6)), 'aram') + ) + ], true), $items); + }); } /** @@ -190,53 +203,55 @@ public function testVariableWithPrefix() */ public function testNewInNamespace() { - $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/used_new.php'); - $this->loader->open($completionUri, file_get_contents($completionUri)); - $items = $this->textDocument->completion( - new TextDocumentIdentifier($completionUri), - new Position(6, 10) - )->wait(); - $this->assertCompletionsListSubset(new CompletionList([ - // Global TestClass definition (inserted as \TestClass) - new CompletionItem( - 'TestClass', - CompletionItemKind::CLASS_, - null, - 'Pariatur ut laborum tempor voluptate consequat ea deserunt.' . "\n\n" . - 'Deserunt enim minim sunt sint ea nisi. Deserunt excepteur tempor id nostrud' . "\n" . - 'laboris commodo ad commodo velit mollit qui non officia id. Nulla duis veniam' . "\n" . - 'veniam officia deserunt et non dolore mollit ea quis eiusmod sit non. Occaecat' . "\n" . - 'consequat sunt culpa exercitation pariatur id reprehenderit nisi incididunt Lorem' . "\n" . - 'sint. Officia culpa pariatur laborum nostrud cupidatat consequat mollit.', - null, - null, - '\TestClass' - ), - new CompletionItem( - 'ChildClass', - CompletionItemKind::CLASS_, - null, - null, - null, - null, - '\ChildClass' - ), - // Namespaced, `use`d TestClass definition (inserted as TestClass) - new CompletionItem( - 'TestClass', - CompletionItemKind::CLASS_, - 'TestNamespace', - 'Pariatur ut laborum tempor voluptate consequat ea deserunt.' . "\n\n" . - 'Deserunt enim minim sunt sint ea nisi. Deserunt excepteur tempor id nostrud' . "\n" . - 'laboris commodo ad commodo velit mollit qui non officia id. Nulla duis veniam' . "\n" . - 'veniam officia deserunt et non dolore mollit ea quis eiusmod sit non. Occaecat' . "\n" . - 'consequat sunt culpa exercitation pariatur id reprehenderit nisi incididunt Lorem' . "\n" . - 'sint. Officia culpa pariatur laborum nostrud cupidatat consequat mollit.', - null, - null, - 'TestClass' - ), - ], true), $items); + Loop::run(function () { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/used_new.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = yield $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(6, 10) + ); + $this->assertCompletionsListSubset(new CompletionList([ + // Global TestClass definition (inserted as \TestClass) + new CompletionItem( + 'TestClass', + CompletionItemKind::CLASS_, + null, + 'Pariatur ut laborum tempor voluptate consequat ea deserunt.' . "\n\n" . + 'Deserunt enim minim sunt sint ea nisi. Deserunt excepteur tempor id nostrud' . "\n" . + 'laboris commodo ad commodo velit mollit qui non officia id. Nulla duis veniam' . "\n" . + 'veniam officia deserunt et non dolore mollit ea quis eiusmod sit non. Occaecat' . "\n" . + 'consequat sunt culpa exercitation pariatur id reprehenderit nisi incididunt Lorem' . "\n" . + 'sint. Officia culpa pariatur laborum nostrud cupidatat consequat mollit.', + null, + null, + '\TestClass' + ), + new CompletionItem( + 'ChildClass', + CompletionItemKind::CLASS_, + null, + null, + null, + null, + '\ChildClass' + ), + // Namespaced, `use`d TestClass definition (inserted as TestClass) + new CompletionItem( + 'TestClass', + CompletionItemKind::CLASS_, + 'TestNamespace', + 'Pariatur ut laborum tempor voluptate consequat ea deserunt.' . "\n\n" . + 'Deserunt enim minim sunt sint ea nisi. Deserunt excepteur tempor id nostrud' . "\n" . + 'laboris commodo ad commodo velit mollit qui non officia id. Nulla duis veniam' . "\n" . + 'veniam officia deserunt et non dolore mollit ea quis eiusmod sit non. Occaecat' . "\n" . + 'consequat sunt culpa exercitation pariatur id reprehenderit nisi incididunt Lorem' . "\n" . + 'sint. Officia culpa pariatur laborum nostrud cupidatat consequat mollit.', + null, + null, + 'TestClass' + ), + ], true), $items); + }); } /** @@ -244,31 +259,33 @@ public function testNewInNamespace() */ public function testUsedClass() { - $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/used_class.php'); - $this->loader->open($completionUri, file_get_contents($completionUri)); - $items = $this->textDocument->completion( - new TextDocumentIdentifier($completionUri), - new Position(6, 5) - )->wait(); - $this->assertCompletionsListSubset(new CompletionList([ - new CompletionItem( - 'TestClass', - CompletionItemKind::CLASS_, - 'TestNamespace', - 'Pariatur ut laborum tempor voluptate consequat ea deserunt.' . "\n\n" . + Loop::run(function () { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/used_class.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = yield $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(6, 5) + ); + $this->assertCompletionsListSubset(new CompletionList([ + new CompletionItem( + 'TestClass', + CompletionItemKind::CLASS_, + 'TestNamespace', + 'Pariatur ut laborum tempor voluptate consequat ea deserunt.' . "\n\n" . 'Deserunt enim minim sunt sint ea nisi. Deserunt excepteur tempor id nostrud' . "\n" . 'laboris commodo ad commodo velit mollit qui non officia id. Nulla duis veniam' . "\n" . 'veniam officia deserunt et non dolore mollit ea quis eiusmod sit non. Occaecat' . "\n" . 'consequat sunt culpa exercitation pariatur id reprehenderit nisi incididunt Lorem' . "\n" . 'sint. Officia culpa pariatur laborum nostrud cupidatat consequat mollit.', - null, - null, - 'TestClass' - ) - ], true), $items); + null, + null, + 'TestClass' + ) + ], true), $items); - $this->assertCompletionsListDoesNotContainLabel('OtherClass', $items); - $this->assertCompletionsListDoesNotContainLabel('TestInterface', $items); + $this->assertCompletionsListDoesNotContainLabel('OtherClass', $items); + $this->assertCompletionsListDoesNotContainLabel('TestInterface', $items); + }); } /** @@ -276,26 +293,28 @@ public function testUsedClass() */ public function testUsedNamespaceWithPrefix() { - $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/used_namespace.php'); - $this->loader->open($completionUri, file_get_contents($completionUri)); - $items = $this->textDocument->completion( - new TextDocumentIdentifier($completionUri), - new Position(8, 16) - )->wait(); - $this->assertEquals( - new CompletionList([ - new CompletionItem( - 'InnerClass', - CompletionItemKind::CLASS_, - 'TestNamespace\\InnerNamespace', - null, - null, - null, - 'AliasNamespace\\InnerClass' - ) - ], true), - $items - ); + Loop::run(function () { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/used_namespace.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = yield $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(8, 16) + ); + $this->assertEquals( + new CompletionList([ + new CompletionItem( + 'InnerClass', + CompletionItemKind::CLASS_, + 'TestNamespace\\InnerNamespace', + null, + null, + null, + 'AliasNamespace\\InnerClass' + ) + ], true), + $items + ); + }); } /** @@ -303,26 +322,28 @@ public function testUsedNamespaceWithPrefix() */ public function testUsedNamespaceWithoutPrefix() { - $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/used_namespace.php'); - $this->loader->open($completionUri, file_get_contents($completionUri)); - $items = $this->textDocument->completion( - new TextDocumentIdentifier($completionUri), - new Position(9, 15) - )->wait(); - $this->assertEquals( - new CompletionList([ - new CompletionItem( - 'InnerClass', - CompletionItemKind::CLASS_, - 'TestNamespace\InnerNamespace', - null, - null, - null, - 'AliasNamespace\InnerClass' - ), - ], true), - $items - ); + Loop::run(function () { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/used_namespace.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = yield $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(9, 15) + ); + $this->assertEquals( + new CompletionList([ + new CompletionItem( + 'InnerClass', + CompletionItemKind::CLASS_, + 'TestNamespace\InnerNamespace', + null, + null, + null, + 'AliasNamespace\InnerClass' + ), + ], true), + $items + ); + }); } /** @@ -330,23 +351,25 @@ public function testUsedNamespaceWithoutPrefix() */ public function testStaticPropertyWithPrefix() { - $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/static_property_with_prefix.php'); - $this->loader->open($completionUri, file_get_contents($completionUri)); - $items = $this->textDocument->completion( - new TextDocumentIdentifier($completionUri), - new Position(2, 14) - )->wait(); - $this->assertCompletionsListSubset(new CompletionList([ - new CompletionItem( - 'staticTestProperty', - CompletionItemKind::PROPERTY, - '\TestClass[]', - 'Lorem excepteur officia sit anim velit veniam enim.', - null, - null, - '$staticTestProperty' - ) - ], true), $items); + Loop::run(function () { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/static_property_with_prefix.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = yield $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(2, 14) + ); + $this->assertCompletionsListSubset(new CompletionList([ + new CompletionItem( + 'staticTestProperty', + CompletionItemKind::PROPERTY, + '\TestClass[]', + 'Lorem excepteur officia sit anim velit veniam enim.', + null, + null, + '$staticTestProperty' + ) + ], true), $items); + }); } /** @@ -354,35 +377,37 @@ public function testStaticPropertyWithPrefix() */ public function testStaticWithoutPrefix() { - $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/static.php'); - $this->loader->open($completionUri, file_get_contents($completionUri)); - $items = $this->textDocument->completion( - new TextDocumentIdentifier($completionUri), - new Position(2, 11) - )->wait(); - $this->assertCompletionsListSubset(new CompletionList([ - new CompletionItem( - 'TEST_CLASS_CONST', - CompletionItemKind::VARIABLE, - 'int', - 'Anim labore veniam consectetur laboris minim quis aute aute esse nulla ad.' - ), - new CompletionItem( - 'staticTestProperty', - CompletionItemKind::PROPERTY, - '\TestClass[]', - 'Lorem excepteur officia sit anim velit veniam enim.', - null, - null, - '$staticTestProperty' - ), - new CompletionItem( - 'staticTestMethod', - CompletionItemKind::METHOD, - 'mixed', // Method return type - 'Do magna consequat veniam minim proident eiusmod incididunt aute proident.' - ) - ], true), $items); + Loop::run(function () { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/static.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = yield $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(2, 11) + ); + $this->assertCompletionsListSubset(new CompletionList([ + new CompletionItem( + 'TEST_CLASS_CONST', + CompletionItemKind::VARIABLE, + 'int', + 'Anim labore veniam consectetur laboris minim quis aute aute esse nulla ad.' + ), + new CompletionItem( + 'staticTestProperty', + CompletionItemKind::PROPERTY, + '\TestClass[]', + 'Lorem excepteur officia sit anim velit veniam enim.', + null, + null, + '$staticTestProperty' + ), + new CompletionItem( + 'staticTestMethod', + CompletionItemKind::METHOD, + 'mixed', // Method return type + 'Do magna consequat veniam minim proident eiusmod incididunt aute proident.' + ) + ], true), $items); + }); } /** @@ -390,20 +415,22 @@ public function testStaticWithoutPrefix() */ public function testStaticMethodWithPrefix() { - $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/static_method_with_prefix.php'); - $this->loader->open($completionUri, file_get_contents($completionUri)); - $items = $this->textDocument->completion( - new TextDocumentIdentifier($completionUri), - new Position(2, 13) - )->wait(); - $this->assertCompletionsListSubset(new CompletionList([ - new CompletionItem( - 'staticTestMethod', - CompletionItemKind::METHOD, - 'mixed', - 'Do magna consequat veniam minim proident eiusmod incididunt aute proident.' - ) - ], true), $items); + Loop::run(function () { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/static_method_with_prefix.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = yield $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(2, 13) + ); + $this->assertCompletionsListSubset(new CompletionList([ + new CompletionItem( + 'staticTestMethod', + CompletionItemKind::METHOD, + 'mixed', + 'Do magna consequat veniam minim proident eiusmod incididunt aute proident.' + ) + ], true), $items); + }); } /** @@ -411,20 +438,22 @@ public function testStaticMethodWithPrefix() */ public function testClassConstWithPrefix() { - $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/class_const_with_prefix.php'); - $this->loader->open($completionUri, file_get_contents($completionUri)); - $items = $this->textDocument->completion( - new TextDocumentIdentifier($completionUri), - new Position(2, 13) - )->wait(); - $this->assertCompletionsListSubset(new CompletionList([ - new CompletionItem( - 'TEST_CLASS_CONST', - CompletionItemKind::VARIABLE, - 'int', - 'Anim labore veniam consectetur laboris minim quis aute aute esse nulla ad.' - ) - ], true), $items); + Loop::run(function () { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/class_const_with_prefix.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = yield $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(2, 13) + ); + $this->assertCompletionsListSubset(new CompletionList([ + new CompletionItem( + 'TEST_CLASS_CONST', + CompletionItemKind::VARIABLE, + 'int', + 'Anim labore veniam consectetur laboris minim quis aute aute esse nulla ad.' + ) + ], true), $items); + }); } /** @@ -432,29 +461,31 @@ public function testClassConstWithPrefix() */ public function testFullyQualifiedClass() { - $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/fully_qualified_class.php'); - $this->loader->open($completionUri, file_get_contents($completionUri)); - $items = $this->textDocument->completion( - new TextDocumentIdentifier($completionUri), - new Position(6, 6) - )->wait(); - $this->assertCompletionsListSubset(new CompletionList([ - new CompletionItem( - 'TestClass', - CompletionItemKind::CLASS_, - null, - 'Pariatur ut laborum tempor voluptate consequat ea deserunt.' . "\n\n" . - 'Deserunt enim minim sunt sint ea nisi. Deserunt excepteur tempor id nostrud' . "\n" . - 'laboris commodo ad commodo velit mollit qui non officia id. Nulla duis veniam' . "\n" . - 'veniam officia deserunt et non dolore mollit ea quis eiusmod sit non. Occaecat' . "\n" . - 'consequat sunt culpa exercitation pariatur id reprehenderit nisi incididunt Lorem' . "\n" . - 'sint. Officia culpa pariatur laborum nostrud cupidatat consequat mollit.' - ) - ], true), $items); - // Assert that all results are non-namespaced. - foreach ($items->items as $item) { - $this->assertSame($item->detail, null); - } + Loop::run(function () { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/fully_qualified_class.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = yield $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(6, 6) + ); + $this->assertCompletionsListSubset(new CompletionList([ + new CompletionItem( + 'TestClass', + CompletionItemKind::CLASS_, + null, + 'Pariatur ut laborum tempor voluptate consequat ea deserunt.' . "\n\n" . + 'Deserunt enim minim sunt sint ea nisi. Deserunt excepteur tempor id nostrud' . "\n" . + 'laboris commodo ad commodo velit mollit qui non officia id. Nulla duis veniam' . "\n" . + 'veniam officia deserunt et non dolore mollit ea quis eiusmod sit non. Occaecat' . "\n" . + 'consequat sunt culpa exercitation pariatur id reprehenderit nisi incididunt Lorem' . "\n" . + 'sint. Officia culpa pariatur laborum nostrud cupidatat consequat mollit.' + ) + ], true), $items); + // Assert that all results are non-namespaced. + foreach ($items->items as $item) { + $this->assertSame($item->detail, null); + } + }); } /** @@ -462,16 +493,18 @@ public function testFullyQualifiedClass() */ public function testKeywords() { - $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/keywords.php'); - $this->loader->open($completionUri, file_get_contents($completionUri)); - $items = $this->textDocument->completion( - new TextDocumentIdentifier($completionUri), - new Position(2, 1) - )->wait(); - $this->assertCompletionsListSubset(new CompletionList([ - new CompletionItem('class', CompletionItemKind::KEYWORD, null, null, null, null, 'class'), - new CompletionItem('clone', CompletionItemKind::KEYWORD, null, null, null, null, 'clone') - ], true), $items); + Loop::run(function () { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/keywords.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = yield $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(2, 1) + ); + $this->assertCompletionsListSubset(new CompletionList([ + new CompletionItem('class', CompletionItemKind::KEYWORD, null, null, null, null, 'class'), + new CompletionItem('clone', CompletionItemKind::KEYWORD, null, null, null, null, 'clone') + ], true), $items); + }); } /** @@ -479,24 +512,26 @@ public function testKeywords() */ public function testHtmlWithoutPrefix() { - $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/html.php'); - $this->loader->open($completionUri, file_get_contents($completionUri)); - $items = $this->textDocument->completion( - new TextDocumentIdentifier($completionUri), - new Position(0, 0) - )->wait(); - $this->assertCompletionsListSubset(new CompletionList([ - new CompletionItem( - 'loader->open($completionUri, file_get_contents($completionUri)); + $items = yield $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(0, 0) + ); + $this->assertCompletionsListSubset(new CompletionList([ + new CompletionItem( + 'loader->open($completionUri, file_get_contents($completionUri)); - $items = $this->textDocument->completion( - new TextDocumentIdentifier($completionUri), - new Position(0, 1) - )->wait(); + Loop::run(function () { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/html_with_prefix.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = yield $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(0, 1) + ); - $this->assertEquals(new CompletionList([], true), $items); + $this->assertEquals(new CompletionList([], true), $items); + }); } /** @@ -519,26 +556,28 @@ public function testHtmlWontBeProposedWithoutCompletionContext() */ public function testHtmlWontBeProposedWithPrefixWithCompletionContext() { - $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/html_with_prefix.php'); - $this->loader->open($completionUri, file_get_contents($completionUri)); - $items = $this->textDocument->completion( - new TextDocumentIdentifier($completionUri), - new Position(0, 1), - new CompletionContext(CompletionTriggerKind::TRIGGER_CHARACTER, '<') - )->wait(); - - $this->assertEquals(new CompletionList([ - new CompletionItem( - 'loader->open($completionUri, file_get_contents($completionUri)); + $items = yield $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(0, 1), + new CompletionContext(CompletionTriggerKind::TRIGGER_CHARACTER, '<') + ); + + $this->assertEquals(new CompletionList([ + new CompletionItem( + 'loader->open($completionUri, file_get_contents($completionUri)); - $items = $this->textDocument->completion( - new TextDocumentIdentifier($completionUri), - new Position(0, 1), - new CompletionContext(CompletionTriggerKind::TRIGGER_CHARACTER, '>') - )->wait(); - $this->assertEquals(new CompletionList([], true), $items); + Loop::run(function () { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/html_no_completion.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = yield $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(0, 1), + new CompletionContext(CompletionTriggerKind::TRIGGER_CHARACTER, '>') + ); + $this->assertEquals(new CompletionList([], true), $items); + }); } /** @@ -561,25 +602,27 @@ public function testHtmlPrefixShouldNotTriggerCompletion() */ public function testHtmlPrefixShouldTriggerCompletionIfManuallyInvoked() { - $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/html_no_completion.php'); - $this->loader->open($completionUri, file_get_contents($completionUri)); - $items = $this->textDocument->completion( - new TextDocumentIdentifier($completionUri), - new Position(0, 1), - new CompletionContext(CompletionTriggerKind::INVOKED) - )->wait(); - $this->assertEquals(new CompletionList([ - new CompletionItem( - 'loader->open($completionUri, file_get_contents($completionUri)); + $items = yield $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(0, 1), + new CompletionContext(CompletionTriggerKind::INVOKED) + ); + $this->assertEquals(new CompletionList([ + new CompletionItem( + 'loader->open($completionUri, file_get_contents($completionUri)); - $items = $this->textDocument->completion( - new TextDocumentIdentifier($completionUri), - new Position(4, 6) - )->wait(); - $this->assertCompletionsListSubset(new CompletionList([ - new CompletionItem( - 'SomeNamespace', - CompletionItemKind::MODULE - ) - ], true), $items); + Loop::run(function () { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/namespace.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = yield $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(4, 6) + ); + $this->assertCompletionsListSubset(new CompletionList([ + new CompletionItem( + 'SomeNamespace', + CompletionItemKind::MODULE + ) + ], true), $items); + }); } /** @@ -606,34 +651,36 @@ public function testNamespace() */ public function testBarePhpVariable() { - $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/bare_php.php'); - $this->loader->open($completionUri, file_get_contents($completionUri)); - $items = $this->textDocument->completion( - new TextDocumentIdentifier($completionUri), - new Position(4, 8) - )->wait(); - $this->assertCompletionsListSubset(new CompletionList([ - new CompletionItem( - '$abc2', - CompletionItemKind::VARIABLE, - 'int', - null, - null, - null, - null, - new TextEdit(new Range(new Position(4, 8), new Position(4, 8)), 'c2') - ), - new CompletionItem( - '$abc', - CompletionItemKind::VARIABLE, - 'int', - null, - null, - null, - null, - new TextEdit(new Range(new Position(4, 8), new Position(4, 8)), 'c') - ) - ], true), $items); + Loop::run(function () { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/bare_php.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = yield $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(4, 8) + ); + $this->assertCompletionsListSubset(new CompletionList([ + new CompletionItem( + '$abc2', + CompletionItemKind::VARIABLE, + 'int', + null, + null, + null, + null, + new TextEdit(new Range(new Position(4, 8), new Position(4, 8)), 'c2') + ), + new CompletionItem( + '$abc', + CompletionItemKind::VARIABLE, + 'int', + null, + null, + null, + null, + new TextEdit(new Range(new Position(4, 8), new Position(4, 8)), 'c') + ) + ], true), $items); + }); } /** @@ -641,13 +688,15 @@ public function testBarePhpVariable() */ public function testForeach(Position $position, array $expectedItems) { - $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/foreach.php'); - $this->loader->open($completionUri, file_get_contents($completionUri)); - $items = $this->textDocument->completion( - new TextDocumentIdentifier($completionUri), - $position - )->wait(); - $this->assertCompletionsListSubset(new CompletionList($expectedItems, true), $items); + Loop::run(function () use ($expectedItems, $position) { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/foreach.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = yield $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + $position + ); + $this->assertCompletionsListSubset(new CompletionList($expectedItems, true), $items); + }); } public function foreachProvider(): array @@ -793,46 +842,50 @@ public function foreachProvider(): array public function testMethodReturnType() { - $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/method_return_type.php'); - $this->loader->open($completionUri, file_get_contents($completionUri)); - $items = $this->textDocument->completion( - new TextDocumentIdentifier($completionUri), - new Position(10, 6) - )->wait(); - $this->assertCompletionsListSubset(new CompletionList([ - new CompletionItem( - 'foo', - CompletionItemKind::METHOD, - '\FooClass', - null, - null, - null, - null, - null - ) - ], true), $items); + Loop::run(function () { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/method_return_type.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = yield $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(10, 6) + ); + $this->assertCompletionsListSubset(new CompletionList([ + new CompletionItem( + 'foo', + CompletionItemKind::METHOD, + '\FooClass', + null, + null, + null, + null, + null + ) + ], true), $items); + }); } public function testStaticMethodReturnType() { - $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/static_method_return_type.php'); - $this->loader->open($completionUri, file_get_contents($completionUri)); - $items = $this->textDocument->completion( - new TextDocumentIdentifier($completionUri), - new Position(11, 6) - )->wait(); - $this->assertCompletionsListSubset(new CompletionList([ - new CompletionItem( - 'bar', - CompletionItemKind::METHOD, - 'mixed', - null, - null, - null, - null, - null - ) - ], true), $items); + Loop::run(function () { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/static_method_return_type.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = yield $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(11, 6) + ); + $this->assertCompletionsListSubset(new CompletionList([ + new CompletionItem( + 'bar', + CompletionItemKind::METHOD, + 'mixed', + null, + null, + null, + null, + null + ) + ], true), $items); + }); } private function assertCompletionsListSubset(CompletionList $subsetList, CompletionList $list) @@ -856,38 +909,40 @@ private function assertCompletionsListDoesNotContainLabel(string $label, Complet */ public function testThisWithoutPrefix() { - $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/this.php'); - $this->loader->open($completionUri, file_get_contents($completionUri)); - $items = $this->textDocument->completion( - new TextDocumentIdentifier($completionUri), - new Position(12, 15) - )->wait(); - $this->assertEquals(new CompletionList([ - new CompletionItem( - 'foo', - CompletionItemKind::PROPERTY, - 'mixed', // Type of the property - null - ), - new CompletionItem( - 'bar', - CompletionItemKind::PROPERTY, - 'mixed', // Type of the property - null - ), - new CompletionItem( - 'method', - CompletionItemKind::METHOD, - 'mixed', // Return type of the method - null - ), - new CompletionItem( - 'test', - CompletionItemKind::METHOD, - 'mixed', // Return type of the method - null - ) - ], true), $items); + Loop::run(function () { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/this.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = yield $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(12, 15) + ); + $this->assertEquals(new CompletionList([ + new CompletionItem( + 'foo', + CompletionItemKind::PROPERTY, + 'mixed', // Type of the property + null + ), + new CompletionItem( + 'bar', + CompletionItemKind::PROPERTY, + 'mixed', // Type of the property + null + ), + new CompletionItem( + 'method', + CompletionItemKind::METHOD, + 'mixed', // Return type of the method + null + ), + new CompletionItem( + 'test', + CompletionItemKind::METHOD, + 'mixed', // Return type of the method + null + ) + ], true), $items); + }); } /** @@ -895,50 +950,52 @@ public function testThisWithoutPrefix() */ public function testThisWithPrefix() { - $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/this_with_prefix.php'); - $this->loader->open($completionUri, file_get_contents($completionUri)); - $items = $this->textDocument->completion( - new TextDocumentIdentifier($completionUri), - new Position(12, 16) - )->wait(); - $this->assertEquals(new CompletionList([ - new CompletionItem( - 'foo', - CompletionItemKind::PROPERTY, - 'mixed', // Type of the property - null - ), - new CompletionItem( - 'bar', - CompletionItemKind::PROPERTY, - 'mixed', // Type of the property - null - ), - new CompletionItem( - 'method', - CompletionItemKind::METHOD, - 'mixed', // Return type of the method - null - ), - new CompletionItem( - 'test', - CompletionItemKind::METHOD, - 'mixed', // Return type of the method - null - ), - new CompletionItem( - 'testProperty', - CompletionItemKind::PROPERTY, - '\TestClass', // Type of the property - 'Reprehenderit magna velit mollit ipsum do.' - ), - new CompletionItem( - 'testMethod', - CompletionItemKind::METHOD, - '\TestClass', // Return type of the method - 'Non culpa nostrud mollit esse sunt laboris in irure ullamco cupidatat amet.' - ), - ], true), $items); + Loop::run(function () { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/this_with_prefix.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = yield $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(12, 16) + ); + $this->assertEquals(new CompletionList([ + new CompletionItem( + 'foo', + CompletionItemKind::PROPERTY, + 'mixed', // Type of the property + null + ), + new CompletionItem( + 'bar', + CompletionItemKind::PROPERTY, + 'mixed', // Type of the property + null + ), + new CompletionItem( + 'method', + CompletionItemKind::METHOD, + 'mixed', // Return type of the method + null + ), + new CompletionItem( + 'test', + CompletionItemKind::METHOD, + 'mixed', // Return type of the method + null + ), + new CompletionItem( + 'testProperty', + CompletionItemKind::PROPERTY, + '\TestClass', // Type of the property + 'Reprehenderit magna velit mollit ipsum do.' + ), + new CompletionItem( + 'testMethod', + CompletionItemKind::METHOD, + '\TestClass', // Return type of the method + 'Non culpa nostrud mollit esse sunt laboris in irure ullamco cupidatat amet.' + ), + ], true), $items); + }); } /** @@ -946,28 +1003,30 @@ public function testThisWithPrefix() */ public function testThisReturnValue() { - $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/this_return_value.php'); - $this->loader->open($completionUri, file_get_contents($completionUri)); - $items = $this->textDocument->completion( - new TextDocumentIdentifier($completionUri), - new Position(17, 23) - )->wait(); - $this->assertEquals(new CompletionList([ - new CompletionItem( - 'bar', - CompletionItemKind::METHOD, - 'mixed' // Return type of the method - ), - new CompletionItem( - 'qux', - CompletionItemKind::METHOD, - 'mixed' // Return type of the method - ), - new CompletionItem( - 'foo', - CompletionItemKind::METHOD, - '$this' // Return type of the method - ), - ], true), $items); + Loop::run(function () { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/this_return_value.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = yield $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(17, 23) + ); + $this->assertEquals(new CompletionList([ + new CompletionItem( + 'bar', + CompletionItemKind::METHOD, + 'mixed' // Return type of the method + ), + new CompletionItem( + 'qux', + CompletionItemKind::METHOD, + 'mixed' // Return type of the method + ), + new CompletionItem( + 'foo', + CompletionItemKind::METHOD, + '$this' // Return type of the method + ), + ], true), $items); + }); } } diff --git a/tests/Server/TextDocument/Definition/GlobalFallbackTest.php b/tests/Server/TextDocument/Definition/GlobalFallbackTest.php index cca6d4d0..7f794287 100644 --- a/tests/Server/TextDocument/Definition/GlobalFallbackTest.php +++ b/tests/Server/TextDocument/Definition/GlobalFallbackTest.php @@ -1,8 +1,9 @@ textDocument->definition( - new TextDocumentIdentifier('global_fallback'), - new Position(9, 16) - )->wait(); - $this->assertEquals([], $result); + Loop::run(function () { + // $obj = new TestClass(); + // Get definition for TestClass should not fall back to global + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier('global_fallback'), + new Position(9, 16) + ); + $this->assertEquals([], $result); + }); } public function testFallsBackForConstants() { - // echo TEST_CONST; - // Get definition for TEST_CONST - $result = $this->textDocument->definition( - new TextDocumentIdentifier('global_fallback'), - new Position(6, 10) - )->wait(); - $this->assertEquals(new Location('global_symbols', new Range(new Position(9, 6), new Position(9, 22))), $result); + Loop::run(function () { + // echo TEST_CONST; + // Get definition for TEST_CONST + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier('global_fallback'), + new Position(6, 10) + ); + $this->assertEquals(new Location('global_symbols', new Range(new Position(9, 6), new Position(9, 22))), $result); + }); } public function testFallsBackForFunctions() { - // test_function(); - // Get definition for test_function - $result = $this->textDocument->definition( - new TextDocumentIdentifier('global_fallback'), - new Position(5, 6) - )->wait(); - $this->assertEquals(new Location('global_symbols', new Range(new Position(78, 0), new Position(81, 1))), $result); + Loop::run(function () { + // test_function(); + // Get definition for test_function + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier('global_fallback'), + new Position(5, 6) + ); + $this->assertEquals(new Location('global_symbols', new Range(new Position(78, 0), new Position(81, 1))), $result); + }); } } diff --git a/tests/Server/TextDocument/Definition/GlobalTest.php b/tests/Server/TextDocument/Definition/GlobalTest.php index fdfb3b29..39d8d88e 100644 --- a/tests/Server/TextDocument/Definition/GlobalTest.php +++ b/tests/Server/TextDocument/Definition/GlobalTest.php @@ -1,8 +1,9 @@ textDocument->definition( - new TextDocumentIdentifier(pathToUri(realpath(__DIR__ . '/../../../../fixtures/references.php'))), - new Position(0, 0) - )->wait(); - $this->assertEquals([], $result); + Loop::run(function () { + // |textDocument->definition( + new TextDocumentIdentifier(pathToUri(realpath(__DIR__ . '/../../../../fixtures/references.php'))), + new Position(0, 0) + ); + $this->assertEquals([], $result); + }); } public function testDefinitionEmptyResult() { - // namespace keyword - $result = $this->textDocument->definition( - new TextDocumentIdentifier(pathToUri(realpath(__DIR__ . '/../../../../fixtures/references.php'))), - new Position(1, 0) - )->wait(); - $this->assertEquals([], $result); + Loop::run(function () { + // namespace keyword + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier(pathToUri(realpath(__DIR__ . '/../../../../fixtures/references.php'))), + new Position(1, 0) + ); + $this->assertEquals([], $result); + }); } public function testDefinitionForSelfKeyword() { - // echo self::TEST_CLASS_CONST; - // Get definition for self - $reference = $this->getReferenceLocations('TestClass')[0]; - $result = $this->textDocument->definition( - new TextDocumentIdentifier($reference->uri), - $reference->range->start - )->wait(); - $this->assertEquals($this->getDefinitionLocation('TestClass'), $result); + Loop::run(function () { + // echo self::TEST_CLASS_CONST; + // Get definition for self + $reference = $this->getReferenceLocations('TestClass')[0]; + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + ); + $this->assertEquals($this->getDefinitionLocation('TestClass'), $result); + }); } public function testDefinitionForClassLike() { - // $obj = new TestClass(); - // Get definition for TestClass - $reference = $this->getReferenceLocations('TestClass')[1]; - $result = $this->textDocument->definition( - new TextDocumentIdentifier($reference->uri), - $reference->range->start - )->wait(); - $this->assertEquals($this->getDefinitionLocation('TestClass'), $result); + Loop::run(function () { + // $obj = new TestClass(); + // Get definition for TestClass + $reference = $this->getReferenceLocations('TestClass')[1]; + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + ); + $this->assertEquals($this->getDefinitionLocation('TestClass'), $result); + }); } public function testDefinitionForClassOnStaticMethodCall() { - // TestClass::staticTestMethod(); - // Get definition for TestClass - $reference = $this->getReferenceLocations('TestClass')[2]; - $result = $this->textDocument->definition( - new TextDocumentIdentifier($reference->uri), - $reference->range->start - )->wait(); - $this->assertEquals($this->getDefinitionLocation('TestClass'), $result); + Loop::run(function () { + // TestClass::staticTestMethod(); + // Get definition for TestClass + $reference = $this->getReferenceLocations('TestClass')[2]; + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + ); + $this->assertEquals($this->getDefinitionLocation('TestClass'), $result); + }); } public function testDefinitionForClassOnStaticPropertyFetch() { - // echo TestClass::$staticTestProperty; - // Get definition for TestClass - $reference = $this->getReferenceLocations('TestClass')[3]; - $result = $this->textDocument->definition( - new TextDocumentIdentifier($reference->uri), - $reference->range->start - )->wait(); - $this->assertEquals($this->getDefinitionLocation('TestClass'), $result); + Loop::run(function () { + // echo TestClass::$staticTestProperty; + // Get definition for TestClass + $reference = $this->getReferenceLocations('TestClass')[3]; + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + ); + $this->assertEquals($this->getDefinitionLocation('TestClass'), $result); + }); } public function testDefinitionForClassOnConstFetch() { - // TestClass::TEST_CLASS_CONST; - // Get definition for TestClass - $reference = $this->getReferenceLocations('TestClass')[4]; - $result = $this->textDocument->definition( - new TextDocumentIdentifier($reference->uri), - $reference->range->start - )->wait(); - $this->assertEquals($this->getDefinitionLocation('TestClass'), $result); + Loop::run(function () { + // TestClass::TEST_CLASS_CONST; + // Get definition for TestClass + $reference = $this->getReferenceLocations('TestClass')[4]; + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + ); + $this->assertEquals($this->getDefinitionLocation('TestClass'), $result); + }); } public function testDefinitionForImplements() { - // class TestClass implements TestInterface - // Get definition for TestInterface - $reference = $this->getReferenceLocations('TestInterface')[0]; - $result = $this->textDocument->definition( - new TextDocumentIdentifier($reference->uri), - $reference->range->start - )->wait(); - $this->assertEquals($this->getDefinitionLocation('TestInterface'), $result); + Loop::run(function () { + // class TestClass implements TestInterface + // Get definition for TestInterface + $reference = $this->getReferenceLocations('TestInterface')[0]; + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + ); + $this->assertEquals($this->getDefinitionLocation('TestInterface'), $result); + }); } public function testDefinitionForClassConstants() { - // echo TestClass::TEST_CLASS_CONST; - // Get definition for TEST_CLASS_CONST - $reference = $this->getReferenceLocations('TestClass::TEST_CLASS_CONST')[1]; - $result = $this->textDocument->definition( - new TextDocumentIdentifier($reference->uri), - $reference->range->end - )->wait(); - $this->assertEquals($this->getDefinitionLocation('TestClass::TEST_CLASS_CONST'), $result); + Loop::run(function () { + // echo TestClass::TEST_CLASS_CONST; + // Get definition for TEST_CLASS_CONST + $reference = $this->getReferenceLocations('TestClass::TEST_CLASS_CONST')[1]; + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + ); + $this->assertEquals($this->getDefinitionLocation('TestClass::TEST_CLASS_CONST'), $result); + }); } public function testDefinitionForClassConstantsOnSelf() { - // echo self::TEST_CLASS_CONST; - // Get definition for TEST_CLASS_CONST - $reference = $this->getReferenceLocations('TestClass::TEST_CLASS_CONST')[0]; - $result = $this->textDocument->definition( - new TextDocumentIdentifier($reference->uri), - $reference->range->end - )->wait(); - $this->assertEquals($this->getDefinitionLocation('TestClass::TEST_CLASS_CONST'), $result); + Loop::run(function () { + // echo self::TEST_CLASS_CONST; + // Get definition for TEST_CLASS_CONST + $reference = $this->getReferenceLocations('TestClass::TEST_CLASS_CONST')[0]; + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + ); + $this->assertEquals($this->getDefinitionLocation('TestClass::TEST_CLASS_CONST'), $result); + }); } public function testDefinitionForConstants() { - // echo TEST_CONST; - // Get definition for TEST_CONST - $reference = $this->getReferenceLocations('TEST_CONST')[1]; - $result = $this->textDocument->definition( - new TextDocumentIdentifier($reference->uri), - $reference->range->start - )->wait(); - $this->assertEquals($this->getDefinitionLocation('TEST_CONST'), $result); + Loop::run(function () { + // echo TEST_CONST; + // Get definition for TEST_CONST + $reference = $this->getReferenceLocations('TEST_CONST')[1]; + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + ); + $this->assertEquals($this->getDefinitionLocation('TEST_CONST'), $result); + }); } public function testDefinitionForStaticMethods() { - // TestClass::staticTestMethod(); - // Get definition for staticTestMethod - $reference = $this->getReferenceLocations('TestClass::staticTestMethod()')[0]; - $result = $this->textDocument->definition( - new TextDocumentIdentifier($reference->uri), - $reference->range->end - )->wait(); - $this->assertEquals($this->getDefinitionLocation('TestClass::staticTestMethod()'), $result); + Loop::run(function () { + // TestClass::staticTestMethod(); + // Get definition for staticTestMethod + $reference = $this->getReferenceLocations('TestClass::staticTestMethod()')[0]; + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + ); + $this->assertEquals($this->getDefinitionLocation('TestClass::staticTestMethod()'), $result); + }); } public function testDefinitionForStaticProperties() { - // echo TestClass::$staticTestProperty; - // Get definition for staticTestProperty - $reference = $this->getReferenceLocations('TestClass::staticTestProperty')[0]; - $result = $this->textDocument->definition( - new TextDocumentIdentifier($reference->uri), - $reference->range->end - )->wait(); - $this->assertEquals($this->getDefinitionLocation('TestClass::staticTestProperty'), $result); + Loop::run(function () { + // echo TestClass::$staticTestProperty; + // Get definition for staticTestProperty + $reference = $this->getReferenceLocations('TestClass::staticTestProperty')[0]; + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + ); + $this->assertEquals($this->getDefinitionLocation('TestClass::staticTestProperty'), $result); + }); } public function testDefinitionForMethods() { - // $obj->testMethod(); - // Get definition for testMethod - $reference = $this->getReferenceLocations('TestClass::testMethod()')[0]; - $result = $this->textDocument->definition( - new TextDocumentIdentifier($reference->uri), - $reference->range->end - )->wait(); - $this->assertEquals($this->getDefinitionLocation('TestClass::testMethod()'), $result); + Loop::run(function () { + // $obj->testMethod(); + // Get definition for testMethod + $reference = $this->getReferenceLocations('TestClass::testMethod()')[0]; + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + ); + $this->assertEquals($this->getDefinitionLocation('TestClass::testMethod()'), $result); + }); } public function testDefinitionForMethodOnChildClass() { - // $child->testMethod(); - // Get definition for testMethod - $reference = $this->getReferenceLocations('TestClass::testMethod()')[2]; - $result = $this->textDocument->definition( - new TextDocumentIdentifier($reference->uri), - $reference->range->end - )->wait(); - $this->assertEquals($this->getDefinitionLocation('TestClass::testMethod()'), $result); + Loop::run(function () { + // $child->testMethod(); + // Get definition for testMethod + $reference = $this->getReferenceLocations('TestClass::testMethod()')[2]; + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + ); + $this->assertEquals($this->getDefinitionLocation('TestClass::testMethod()'), $result); + }); } public function testDefinitionForProperties() { - // echo $obj->testProperty; - // Get definition for testProperty - $reference = $this->getReferenceLocations('TestClass::testProperty')[1]; - $result = $this->textDocument->definition( - new TextDocumentIdentifier($reference->uri), - $reference->range->end - )->wait(); - $this->assertEquals($this->getDefinitionLocation('TestClass::testProperty'), $result); + Loop::run(function () { + // echo $obj->testProperty; + // Get definition for testProperty + $reference = $this->getReferenceLocations('TestClass::testProperty')[1]; + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + ); + $this->assertEquals($this->getDefinitionLocation('TestClass::testProperty'), $result); + }); } public function testDefinitionForPropertiesOnThis() { - // $this->testProperty = $testParameter; - // Get definition for testProperty - $reference = $this->getReferenceLocations('TestClass::testProperty')[0]; - $result = $this->textDocument->definition( - new TextDocumentIdentifier($reference->uri), - $reference->range->end - )->wait(); - $this->assertEquals($this->getDefinitionLocation('TestClass::testProperty'), $result); + Loop::run(function () { + // $this->testProperty = $testParameter; + // Get definition for testProperty + $reference = $this->getReferenceLocations('TestClass::testProperty')[0]; + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + ); + $this->assertEquals($this->getDefinitionLocation('TestClass::testProperty'), $result); + }); } public function testDefinitionForVariables() { - // echo $var; - // Get definition for $var - $uri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/references.php')); - $result = $this->textDocument->definition( - new TextDocumentIdentifier($uri), - new Position(13, 7) - )->wait(); - $this->assertEquals(new Location($uri, new Range(new Position(12, 0), new Position(12, 10))), $result); + Loop::run(function () { + // echo $var; + // Get definition for $var + $uri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/references.php')); + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier($uri), + new Position(13, 7) + ); + $this->assertEquals(new Location($uri, new Range(new Position(12, 0), new Position(12, 10))), $result); + }); } public function testDefinitionForParamTypeHints() { - // function whatever(TestClass $param) { - // Get definition for TestClass - $reference = $this->getReferenceLocations('TestClass')[5]; - $result = $this->textDocument->definition( - new TextDocumentIdentifier($reference->uri), - $reference->range->start - )->wait(); - $this->assertEquals($this->getDefinitionLocation('TestClass'), $result); + Loop::run(function () { + // function whatever(TestClass $param) { + // Get definition for TestClass + $reference = $this->getReferenceLocations('TestClass')[5]; + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + ); + $this->assertEquals($this->getDefinitionLocation('TestClass'), $result); + }); } public function testDefinitionForReturnTypeHints() { - // function whatever(TestClass $param): TestClass { - // Get definition for TestClass - $reference = $this->getReferenceLocations('TestClass')[6]; - $result = $this->textDocument->definition( - new TextDocumentIdentifier($reference->uri), - $reference->range->start - )->wait(); - $this->assertEquals($this->getDefinitionLocation('TestClass'), $result); + Loop::run(function () { + // function whatever(TestClass $param): TestClass { + // Get definition for TestClass + $reference = $this->getReferenceLocations('TestClass')[6]; + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + ); + $this->assertEquals($this->getDefinitionLocation('TestClass'), $result); + }); } public function testDefinitionForMethodReturnTypeHints() { - // public function testMethod($testParameter): TestInterface - // Get definition for TestInterface - $reference = $this->getReferenceLocations('TestInterface')[1]; - $result = $this->textDocument->definition( - new TextDocumentIdentifier($reference->uri), - $reference->range->start - )->wait(); - $this->assertEquals($this->getDefinitionLocation('TestInterface'), $result); + Loop::run(function () { + // public function testMethod($testParameter): TestInterface + // Get definition for TestInterface + $reference = $this->getReferenceLocations('TestInterface')[1]; + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + ); + $this->assertEquals($this->getDefinitionLocation('TestInterface'), $result); + }); } public function testDefinitionForParams() { - // echo $param; - // Get definition for $param - $uri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/references.php')); - $result = $this->textDocument->definition( - new TextDocumentIdentifier($uri), - new Position(22, 13) - )->wait(); - $this->assertEquals(new Location($uri, new Range(new Position(21, 18), new Position(21, 34))), $result); + Loop::run(function () { + // echo $param; + // Get definition for $param + $uri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/references.php')); + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier($uri), + new Position(22, 13) + ); + $this->assertEquals(new Location($uri, new Range(new Position(21, 18), new Position(21, 34))), $result); + }); } public function testDefinitionForUsedVariables() { - // echo $var; - // Get definition for $var - $uri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/references.php')); - $result = $this->textDocument->definition( - new TextDocumentIdentifier($uri), - new Position(26, 11) - )->wait(); - $this->assertEquals(new Location($uri, new Range(new Position(25, 22), new Position(25, 26))), $result); + Loop::run(function () { + // echo $var; + // Get definition for $var + $uri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/references.php')); + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier($uri), + new Position(26, 11) + ); + $this->assertEquals(new Location($uri, new Range(new Position(25, 22), new Position(25, 26))), $result); + }); } public function testDefinitionForFunctions() { - // test_function(); - // Get definition for test_function - $reference = $this->getReferenceLocations('test_function()')[0]; - $result = $this->textDocument->definition( - new TextDocumentIdentifier($reference->uri), - $reference->range->start - )->wait(); - $this->assertEquals($this->getDefinitionLocation('test_function()'), $result); + Loop::run(function () { + // test_function(); + // Get definition for test_function + $reference = $this->getReferenceLocations('test_function()')[0]; + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + ); + $this->assertEquals($this->getDefinitionLocation('test_function()'), $result); + }); } public function testDefinitionForUseFunctions() { - // use function test_function; - // Get definition for test_function - $reference = $this->getReferenceLocations('test_function()')[1]; - $result = $this->textDocument->definition( - new TextDocumentIdentifier($reference->uri), - $reference->range->start - )->wait(); - $this->assertEquals($this->getDefinitionLocation('test_function()'), $result); + Loop::run(function () { + // use function test_function; + // Get definition for test_function + $reference = $this->getReferenceLocations('test_function()')[1]; + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + ); + $this->assertEquals($this->getDefinitionLocation('test_function()'), $result); + }); } public function testDefinitionForInstanceOf() { - // if ($abc instanceof TestInterface) { - // Get definition for TestInterface - $reference = $this->getReferenceLocations('TestInterface')[2]; - $result = $this->textDocument->definition( - new TextDocumentIdentifier($reference->uri), - $reference->range->start - )->wait(); - $this->assertEquals($this->getDefinitionLocation('TestInterface'), $result); + Loop::run(function () { + // if ($abc instanceof TestInterface) { + // Get definition for TestInterface + $reference = $this->getReferenceLocations('TestInterface')[2]; + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + ); + $this->assertEquals($this->getDefinitionLocation('TestInterface'), $result); + }); } public function testDefinitionForNestedMethodCall() { - // $obj->testProperty->testMethod(); - // Get definition for testMethod - $reference = $this->getReferenceLocations('TestClass::testMethod()')[1]; - $result = $this->textDocument->definition( - new TextDocumentIdentifier($reference->uri), - $reference->range->end - )->wait(); - $this->assertEquals($this->getDefinitionLocation('TestClass::testMethod()'), $result); + Loop::run(function () { + // $obj->testProperty->testMethod(); + // Get definition for testMethod + $reference = $this->getReferenceLocations('TestClass::testMethod()')[1]; + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + ); + $this->assertEquals($this->getDefinitionLocation('TestClass::testMethod()'), $result); + }); } public function testDefinitionForPropertyFetchOnArrayDimFetch() { - // TestClass::$staticTestProperty[123]->testProperty; - // Get definition for testProperty - $reference = $this->getReferenceLocations('TestClass::testProperty')[3]; - $result = $this->textDocument->definition( - new TextDocumentIdentifier($reference->uri), - $reference->range->end - )->wait(); - $this->assertEquals($this->getDefinitionLocation('TestClass::testProperty'), $result); + Loop::run(function () { + // TestClass::$staticTestProperty[123]->testProperty; + // Get definition for testProperty + $reference = $this->getReferenceLocations('TestClass::testProperty')[3]; + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + ); + $this->assertEquals($this->getDefinitionLocation('TestClass::testProperty'), $result); + }); } } diff --git a/tests/Server/TextDocument/Definition/NamespacedTest.php b/tests/Server/TextDocument/Definition/NamespacedTest.php index 0f9a582b..17be8ace 100644 --- a/tests/Server/TextDocument/Definition/NamespacedTest.php +++ b/tests/Server/TextDocument/Definition/NamespacedTest.php @@ -1,8 +1,9 @@ getReferenceLocations('TEST_CONST')[0]; - $result = $this->textDocument->definition( - new TextDocumentIdentifier($reference->uri), - $reference->range->start - )->wait(); - $this->assertEquals($this->getDefinitionLocation('TEST_CONST'), $result); + Loop::run(function () { + // echo TEST_CONST; + // Get definition for TEST_CONST + $reference = $this->getReferenceLocations('TEST_CONST')[0]; + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + ); + $this->assertEquals($this->getDefinitionLocation('TEST_CONST'), $result); + }); } public function testDefinitionForClassLikeUseStatement() { - // use TestNamespace\TestClass; - // Get definition for TestClass - $reference = $this->getReferenceLocations('TestClass')[7]; - $result = $this->textDocument->definition( - new TextDocumentIdentifier($reference->uri), - $reference->range->start - )->wait(); - $this->assertEquals($this->getDefinitionLocation('TestClass'), $result); + Loop::run(function () { + // use TestNamespace\TestClass; + // Get definition for TestClass + $reference = $this->getReferenceLocations('TestClass')[7]; + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + ); + $this->assertEquals($this->getDefinitionLocation('TestClass'), $result); + }); } public function testDefinitionForClassLikeGroupUseStatement() { - // use TestNamespace\{TestTrait, TestInterface}; - // Get definition for TestInterface - $reference = $this->getReferenceLocations('TestClass')[1]; - $result = $this->textDocument->definition( - new TextDocumentIdentifier($reference->uri), - $reference->range->start - )->wait(); - $this->assertEquals($this->getDefinitionLocation('TestClass'), $result); + Loop::run(function () { + // use TestNamespace\{TestTrait, TestInterface}; + // Get definition for TestInterface + $reference = $this->getReferenceLocations('TestClass')[1]; + $result = yield $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + ); + $this->assertEquals($this->getDefinitionLocation('TestClass'), $result); + }); } } diff --git a/tests/Server/TextDocument/DidChangeTest.php b/tests/Server/TextDocument/DidChangeTest.php index 0f92ed7f..b5bd55c7 100644 --- a/tests/Server/TextDocument/DidChangeTest.php +++ b/tests/Server/TextDocument/DidChangeTest.php @@ -1,8 +1,9 @@ open('whatever', "open('whatever', "range = new Range(new Position(0, 0), new Position(9999, 9999)); - $changeEvent->rangeLength = 9999; - $changeEvent->text = "range = new Range(new Position(0, 0), new Position(9999, 9999)); + $changeEvent->rangeLength = 9999; + $changeEvent->text = "didChange($identifier, [$changeEvent]); + yield $textDocument->didChange($identifier, [$changeEvent]); - $this->assertEquals("getContent()); + $this->assertEquals("getContent()); + }); } } diff --git a/tests/Server/TextDocument/DidCloseTest.php b/tests/Server/TextDocument/DidCloseTest.php index 22eb5349..2c08bf55 100644 --- a/tests/Server/TextDocument/DidCloseTest.php +++ b/tests/Server/TextDocument/DidCloseTest.php @@ -1,8 +1,9 @@ open('whatever', "open('whatever', "uri = 'whatever'; - $textDocumentItem->languageId = 'php'; - $textDocumentItem->version = 1; - $textDocumentItem->text = 'hello world'; - $textDocument->didOpen($textDocumentItem); + $textDocumentItem = new TextDocumentItem(); + $textDocumentItem->uri = 'whatever'; + $textDocumentItem->languageId = 'php'; + $textDocumentItem->version = 1; + $textDocumentItem->text = 'hello world'; + $textDocument->didOpen($textDocumentItem); - $textDocument->didClose(new TextDocumentIdentifier($textDocumentItem->uri)); + $textDocument->didClose(new TextDocumentIdentifier($textDocumentItem->uri)); - $this->assertFalse($loader->isOpen($textDocumentItem->uri)); + $this->assertFalse($loader->isOpen($textDocumentItem->uri)); + }); } } diff --git a/tests/Server/TextDocument/DocumentSymbolTest.php b/tests/Server/TextDocument/DocumentSymbolTest.php index 1267fd00..ae5d1b15 100644 --- a/tests/Server/TextDocument/DocumentSymbolTest.php +++ b/tests/Server/TextDocument/DocumentSymbolTest.php @@ -1,8 +1,9 @@ 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'), - new SymbolInformation('staticTestProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestNamespace\\TestClass::staticTestProperty'), 'TestNamespace\\TestClass'), - new SymbolInformation('testProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestNamespace\\TestClass::testProperty'), 'TestNamespace\\TestClass'), - new SymbolInformation('staticTestMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestNamespace\\TestClass::staticTestMethod()'), 'TestNamespace\\TestClass'), - new SymbolInformation('testMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestNamespace\\TestClass::testMethod()'), 'TestNamespace\\TestClass'), - new SymbolInformation('TestTrait', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\TestTrait'), 'TestNamespace'), - new SymbolInformation('TestInterface', SymbolKind::INTERFACE, $this->getDefinitionLocation('TestNamespace\\TestInterface'), 'TestNamespace'), - new SymbolInformation('test_function', SymbolKind::FUNCTION, $this->getDefinitionLocation('TestNamespace\\test_function()'), 'TestNamespace'), - new SymbolInformation('ChildClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\ChildClass'), 'TestNamespace'), - new SymbolInformation('Example', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\Example'), 'TestNamespace'), - new SymbolInformation('__construct', SymbolKind::CONSTRUCTOR, $this->getDefinitionLocation('TestNamespace\\Example::__construct'), 'TestNamespace\\Example'), - new SymbolInformation('__destruct', SymbolKind::CONSTRUCTOR, $this->getDefinitionLocation('TestNamespace\\Example::__destruct'), 'TestNamespace\\Example'), - new SymbolInformation('TestNamespace\\InnerNamespace', SymbolKind::NAMESPACE, $this->getDefinitionLocation('TestNamespace\\InnerNamespace'), 'TestNamespace'), - new SymbolInformation('InnerClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\InnerNamespace\\InnerClass'), 'TestNamespace\\InnerNamespace'), - ], $result); - // @codingStandardsIgnoreEnd + Loop::run(function () { + // Request symbols + $uri = pathToUri(realpath(__DIR__ . '/../../../fixtures/symbols.php')); + $result = yield $this->textDocument->documentSymbol(new TextDocumentIdentifier($uri)); + // @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'), + new SymbolInformation('staticTestProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestNamespace\\TestClass::staticTestProperty'), 'TestNamespace\\TestClass'), + new SymbolInformation('testProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestNamespace\\TestClass::testProperty'), 'TestNamespace\\TestClass'), + new SymbolInformation('staticTestMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestNamespace\\TestClass::staticTestMethod()'), 'TestNamespace\\TestClass'), + new SymbolInformation('testMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestNamespace\\TestClass::testMethod()'), 'TestNamespace\\TestClass'), + new SymbolInformation('TestTrait', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\TestTrait'), 'TestNamespace'), + new SymbolInformation('TestInterface', SymbolKind::INTERFACE, $this->getDefinitionLocation('TestNamespace\\TestInterface'), 'TestNamespace'), + new SymbolInformation('test_function', SymbolKind::FUNCTION, $this->getDefinitionLocation('TestNamespace\\test_function()'), 'TestNamespace'), + new SymbolInformation('ChildClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\ChildClass'), 'TestNamespace'), + new SymbolInformation('Example', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\Example'), 'TestNamespace'), + new SymbolInformation('__construct', SymbolKind::CONSTRUCTOR, $this->getDefinitionLocation('TestNamespace\\Example::__construct'), 'TestNamespace\\Example'), + new SymbolInformation('__destruct', SymbolKind::CONSTRUCTOR, $this->getDefinitionLocation('TestNamespace\\Example::__destruct'), 'TestNamespace\\Example'), + new SymbolInformation('TestNamespace\\InnerNamespace', SymbolKind::NAMESPACE, $this->getDefinitionLocation('TestNamespace\\InnerNamespace'), 'TestNamespace'), + new SymbolInformation('InnerClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\InnerNamespace\\InnerClass'), 'TestNamespace\\InnerNamespace'), + ], $result); + // @codingStandardsIgnoreEnd + }); } } diff --git a/tests/Server/TextDocument/HoverTest.php b/tests/Server/TextDocument/HoverTest.php index 42b69c81..21b5e5f3 100644 --- a/tests/Server/TextDocument/HoverTest.php +++ b/tests/Server/TextDocument/HoverTest.php @@ -1,8 +1,9 @@ getReferenceLocations('TestClass')[1]; - $result = $this->textDocument->hover( - new TextDocumentIdentifier($reference->uri), - $reference->range->start - )->wait(); - $this->assertEquals(new Hover([ - new MarkedString('php', "range), $result); + Loop::run(function () { + // $obj = new TestClass(); + // Get hover for TestClass + $reference = $this->getReferenceLocations('TestClass')[1]; + $result = yield $this->textDocument->hover( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + ); + $this->assertEquals(new Hover([ + new MarkedString('php', "range), $result); + }); } public function testHoverForClassLikeDefinition() { - // class TestClass implements TestInterface - // Get hover for TestClass - $definition = $this->getDefinitionLocation('TestClass'); - $result = $this->textDocument->hover( - new TextDocumentIdentifier($definition->uri), - $definition->range->start - )->wait(); - $this->assertEquals(new Hover([ - new MarkedString('php', "range), $result); + Loop::run(function () { + // class TestClass implements TestInterface + // Get hover for TestClass + $definition = $this->getDefinitionLocation('TestClass'); + $result = yield $this->textDocument->hover( + new TextDocumentIdentifier($definition->uri), + $definition->range->start + ); + $this->assertEquals(new Hover([ + new MarkedString('php', "range), $result); + }); } public function testHoverForMethod() { - // $obj->testMethod(); - // Get hover for testMethod - $reference = $this->getReferenceLocations('TestClass::testMethod()')[0]; - $result = $this->textDocument->hover( - new TextDocumentIdentifier($reference->uri), - $reference->range->end - )->wait(); - $this->assertEquals(new Hover([ - new MarkedString('php', "range), $result); + Loop::run(function () { + // $obj->testMethod(); + // Get hover for testMethod + $reference = $this->getReferenceLocations('TestClass::testMethod()')[0]; + $result = yield $this->textDocument->hover( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + ); + $this->assertEquals(new Hover([ + new MarkedString('php', "range), $result); + }); } public function testHoverForProperty() { - // echo $obj->testProperty; - // Get hover for testProperty - $reference = $this->getReferenceLocations('TestClass::testProperty')[0]; - $result = $this->textDocument->hover( - new TextDocumentIdentifier($reference->uri), - $reference->range->end - )->wait(); - $this->assertEquals(new Hover([ - new MarkedString('php', "range), $result); + Loop::run(function () { + // echo $obj->testProperty; + // Get hover for testProperty + $reference = $this->getReferenceLocations('TestClass::testProperty')[0]; + $result = yield $this->textDocument->hover( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + ); + $this->assertEquals(new Hover([ + new MarkedString('php', "range), $result); + }); } public function testHoverForStaticMethod() { - // TestClass::staticTestMethod(); - // Get hover for staticTestMethod - $reference = $this->getReferenceLocations('TestClass::staticTestMethod()')[0]; - $result = $this->textDocument->hover( - new TextDocumentIdentifier($reference->uri), - $reference->range->end - )->wait(); - $this->assertEquals(new Hover([ - new MarkedString('php', "range), $result); + Loop::run(function () { + // TestClass::staticTestMethod(); + // Get hover for staticTestMethod + $reference = $this->getReferenceLocations('TestClass::staticTestMethod()')[0]; + $result = yield $this->textDocument->hover( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + ); + $this->assertEquals(new Hover([ + new MarkedString('php', "range), $result); + }); } public function testHoverForStaticProperty() { - // echo TestClass::staticTestProperty; - // Get hover for staticTestProperty - $reference = $this->getReferenceLocations('TestClass::staticTestProperty')[0]; - $result = $this->textDocument->hover( - new TextDocumentIdentifier($reference->uri), - $reference->range->end - )->wait(); - $this->assertEquals(new Hover([ - new MarkedString('php', "range), $result); + Loop::run(function () { + // echo TestClass::staticTestProperty; + // Get hover for staticTestProperty + $reference = $this->getReferenceLocations('TestClass::staticTestProperty')[0]; + $result = yield $this->textDocument->hover( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + ); + $this->assertEquals(new Hover([ + new MarkedString('php', "range), $result); + }); } public function testHoverForClassConstant() { - // echo TestClass::TEST_CLASS_CONST; - // Get hover for TEST_CLASS_CONST - $reference = $this->getReferenceLocations('TestClass::TEST_CLASS_CONST')[0]; - $result = $this->textDocument->hover( - new TextDocumentIdentifier($reference->uri), - $reference->range->end - )->wait(); - $this->assertEquals(new Hover([ - new MarkedString('php', "range), $result); + Loop::run(function () { + // echo TestClass::TEST_CLASS_CONST; + // Get hover for TEST_CLASS_CONST + $reference = $this->getReferenceLocations('TestClass::TEST_CLASS_CONST')[0]; + $result = yield $this->textDocument->hover( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + ); + $this->assertEquals(new Hover([ + new MarkedString('php', "range), $result); + }); } public function testHoverForFunction() { - // test_function(); - // Get hover for test_function - $reference = $this->getReferenceLocations('test_function()')[0]; - $result = $this->textDocument->hover( - new TextDocumentIdentifier($reference->uri), - $reference->range->end - )->wait(); - $this->assertEquals(new Hover([ - new MarkedString('php', "range), $result); + Loop::run(function () { + // test_function(); + // Get hover for test_function + $reference = $this->getReferenceLocations('test_function()')[0]; + $result = yield $this->textDocument->hover( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + ); + $this->assertEquals(new Hover([ + new MarkedString('php', "range), $result); + }); } public function testHoverForConstant() { - // echo TEST_CONST; - // Get hover for TEST_CONST - $reference = $this->getReferenceLocations('TEST_CONST')[0]; - $result = $this->textDocument->hover( - new TextDocumentIdentifier($reference->uri), - $reference->range->end - )->wait(); - $this->assertEquals(new Hover([ - new MarkedString('php', "range), $result); + Loop::run(function () { + // echo TEST_CONST; + // Get hover for TEST_CONST + $reference = $this->getReferenceLocations('TEST_CONST')[0]; + $result = yield $this->textDocument->hover( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + ); + $this->assertEquals(new Hover([ + new MarkedString('php', "range), $result); + }); } public function testHoverForGlobalConstant() { - // print TEST_DEFINE_CONSTANT ? 'true' : 'false'; - // Get hover for TEST_DEFINE_CONSTANT - $reference = $this->getReferenceLocations('TEST_DEFINE_CONSTANT')[0]; - $result = $this->textDocument->hover( - new TextDocumentIdentifier($reference->uri), - $reference->range->end - )->wait(); - // TODO - should pretty print with fqns, like \define, \false. Not yet supported by tolerant-php-parser - $this->assertEquals(new Hover([ - new MarkedString('php', "range), $result); + Loop::run(function () { + // print TEST_DEFINE_CONSTANT ? 'true' : 'false'; + // Get hover for TEST_DEFINE_CONSTANT + $reference = $this->getReferenceLocations('TEST_DEFINE_CONSTANT')[0]; + $result = yield $this->textDocument->hover( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + ); + // TODO - should pretty print with fqns, like \define, \false. Not yet supported by tolerant-php-parser + $this->assertEquals(new Hover([ + new MarkedString('php', "range), $result); + }); } public function testHoverForVariable() { - // echo $var; - // Get hover for $var - $uri = pathToUri(realpath(__DIR__ . '/../../../fixtures/references.php')); - $result = $this->textDocument->hover(new TextDocumentIdentifier($uri), new Position(13, 7))->wait(); - $this->assertEquals(new Hover( - [new MarkedString('php', "textDocument->hover( + new TextDocumentIdentifier($uri), + new Position(13, 7) + ); + $this->assertEquals(new Hover( + [new MarkedString('php', "textDocument->hover(new TextDocumentIdentifier($uri), new Position(22, 11))->wait(); - $this->assertEquals(new Hover( - [ - new MarkedString('php', "textDocument->hover( + new TextDocumentIdentifier($uri), + new Position(22, 11) + ); + $this->assertEquals(new Hover( + [ + new MarkedString('php', "textDocument->hover(new TextDocumentIdentifier($uri), new Position(59, 11))->wait(); - $this->assertEquals(new Hover([ - new MarkedString('php', "textDocument->hover( + new TextDocumentIdentifier($uri), + new Position(59, 11) + ); + $this->assertEquals(new Hover([ + new MarkedString('php', "textDocument = new class($this->args) extends Client\TextDocument { + $this->args = new Deferred(); + $client->textDocument = new class($this->args) extends Client\TextDocument + { + /** @var Deferred */ private $args; - public function __construct(&$args) + + public function __construct($args) { parent::__construct(new ClientHandler(new MockProtocolStream, new MockProtocolStream), new JsonMapper); - $this->args = &$args; + $this->args = $args; } - public function publishDiagnostics(string $uri, array $diagnostics): Promise + + public function publishDiagnostics(string $uri, array $diagnostics): \Generator { - $this->args = func_get_args(); - return Promise\resolve(null); + $this->args->resolve(func_get_args()); + yield new Delayed(0); + return null; } }; $projectIndex = new ProjectIndex(new Index, new DependenciesIndex); @@ -57,98 +66,102 @@ private function openFile($file) public function testParseErrorsArePublishedAsDiagnostics() { - $this->openFile(__DIR__ . '/../../../fixtures/invalid_file.php'); - $this->assertEquals([ - 'whatever', - [[ - 'range' => [ - 'start' => [ - 'line' => 2, - 'character' => 9 + Loop::run(function () { + $this->openFile(__DIR__ . '/../../../fixtures/invalid_file.php'); + $this->assertEquals([ + 'whatever', + [[ + 'range' => [ + 'start' => [ + 'line' => 2, + 'character' => 9 + ], + 'end' => [ + 'line' => 2, + 'character' => 9 + ] ], - 'end' => [ - 'line' => 2, - 'character' => 9 - ] + 'severity' => DiagnosticSeverity::ERROR, + 'code' => null, + 'source' => 'php', + 'message' => "'Name' expected." ], - 'severity' => DiagnosticSeverity::ERROR, - 'code' => null, - 'source' => 'php', - 'message' => "'Name' expected." - ], - [ - 'range' => [ - 'start' => [ - 'line' => 2, - 'character' => 9 + [ + 'range' => [ + 'start' => [ + 'line' => 2, + 'character' => 9 + ], + 'end' => [ + 'line' => 2, + 'character' => 9 + ] + ], + 'severity' => DiagnosticSeverity::ERROR, + 'code' => null, + 'source' => 'php', + 'message' => "'{' expected." ], - 'end' => [ - 'line' => 2, - 'character' => 9 - ] - ], - 'severity' => DiagnosticSeverity::ERROR, - 'code' => null, - 'source' => 'php', - 'message' => "'{' expected." - ], - [ - 'range' => [ - 'start' => [ - 'line' => 2, - 'character' => 9 + [ + 'range' => [ + 'start' => [ + 'line' => 2, + 'character' => 9 + ], + 'end' => [ + 'line' => 2, + 'character' => 9 + ] + ], + 'severity' => DiagnosticSeverity::ERROR, + 'code' => null, + 'source' => 'php', + 'message' => "'}' expected." ], - 'end' => [ - 'line' => 2, - 'character' => 9 - ] - ], - 'severity' => DiagnosticSeverity::ERROR, - 'code' => null, - 'source' => 'php', - 'message' => "'}' expected." - ], - [ - 'range' => [ - 'start' => [ - 'line' => 2, - 'character' => 15 - ], - 'end' => [ - 'line' => 2, - 'character' => 15 - ] - ], - 'severity' => DiagnosticSeverity::ERROR, - 'code' => null, - 'source' => 'php', - 'message' => "'Name' expected." - ]] - ], json_decode(json_encode($this->args), true)); + [ + 'range' => [ + 'start' => [ + 'line' => 2, + 'character' => 15 + ], + 'end' => [ + 'line' => 2, + 'character' => 15 + ] + ], + 'severity' => DiagnosticSeverity::ERROR, + 'code' => null, + 'source' => 'php', + 'message' => "'Name' expected." + ]] + ], json_decode(json_encode(yield $this->args->promise()), true)); + }); } public function testParseErrorsWithOnlyStartLine() { - $this->markTestIncomplete('This diagnostic not yet implemented in tolerant-php-parser'); - $this->openFile(__DIR__ . '/../../../fixtures/namespace_not_first.php'); - $this->assertEquals([ - 'whatever', - [[ - 'range' => [ - 'start' => [ - 'line' => 4, - 'character' => 0 + Loop::run(function () { + $this->markTestIncomplete('This diagnostic not yet implemented in tolerant-php-parser'); + yield $this->openFile(__DIR__ . '/../../../fixtures/namespace_not_first.php'); + $this->assertEquals([ + 'whatever', + [[ + 'range' => [ + 'start' => [ + 'line' => 4, + 'character' => 0 + ], + 'end' => [ + 'line' => 4, + 'character' => 0 + ] ], - 'end' => [ - 'line' => 4, - 'character' => 0 - ] - ], - 'severity' => DiagnosticSeverity::ERROR, - 'code' => null, - 'source' => 'php', - 'message' => "Namespace declaration statement has to be the very first statement in the script" - ]] - ], json_decode(json_encode($this->args), true)); + 'severity' => DiagnosticSeverity::ERROR, + 'code' => null, + 'source' => 'php', + 'message' => "Namespace declaration statement has to be the very first statement in the script" + ]] + ], json_decode(json_encode(yield $this->args->promise()), true)); + }); } } diff --git a/tests/Server/TextDocument/References/GlobalFallbackTest.php b/tests/Server/TextDocument/References/GlobalFallbackTest.php index b1cbdac3..2e75280b 100644 --- a/tests/Server/TextDocument/References/GlobalFallbackTest.php +++ b/tests/Server/TextDocument/References/GlobalFallbackTest.php @@ -1,8 +1,9 @@ setComplete(); - $definitionResolver = new DefinitionResolver($projectIndex); - $client = new LanguageClient(new MockProtocolStream, new MockProtocolStream); + $definitionResolver = new DefinitionResolver($projectIndex); + $client = new LanguageClient(new MockProtocolStream, new MockProtocolStream); $this->documentLoader = new PhpDocumentLoader(new FileSystemContentRetriever, $projectIndex, $definitionResolver); - $this->textDocument = new Server\TextDocument($this->documentLoader, $definitionResolver, $client, $projectIndex); + $this->textDocument = new Server\TextDocument($this->documentLoader, $definitionResolver, $client, $projectIndex); $this->documentLoader->open('global_fallback', file_get_contents(__DIR__ . '/../../../../fixtures/global_fallback.php')); $this->documentLoader->open('global_symbols', file_get_contents(__DIR__ . '/../../../../fixtures/global_symbols.php')); } public function testClassDoesNotFallback() { - // class TestClass implements TestInterface - // Get references for TestClass - $result = $this->textDocument->references( - new ReferenceContext, - new TextDocumentIdentifier('global_symbols'), - new Position(6, 9) - )->wait(); - $this->assertEquals([], $result); + Loop::run(function () { + // class TestClass implements TestInterface + // Get references for TestClass + $result = yield $this->textDocument->references( + new ReferenceContext, + new TextDocumentIdentifier('global_symbols'), + new Position(6, 9) + ); + $this->assertEquals([], $result); + }); } public function testFallsBackForConstants() { - // const TEST_CONST = 123; - // Get references for TEST_CONST - $result = $this->textDocument->references( - new ReferenceContext, - new TextDocumentIdentifier('global_symbols'), - new Position(9, 13) - )->wait(); - $this->assertEquals([new Location('global_fallback', new Range(new Position(6, 5), new Position(6, 15)))], $result); + Loop::run(function () { + // const TEST_CONST = 123; + // Get references for TEST_CONST + $result = yield $this->textDocument->references( + new ReferenceContext, + new TextDocumentIdentifier('global_symbols'), + new Position(9, 13) + ); + $this->assertEquals([new Location('global_fallback', new Range(new Position(6, 5), new Position(6, 15)))], $result); + }); } public function testFallsBackForFunctions() { - // function test_function() - // Get references for test_function - $result = $this->textDocument->references( - new ReferenceContext, - new TextDocumentIdentifier('global_symbols'), - new Position(78, 16) - )->wait(); - $this->assertEquals([new Location('global_fallback', new Range(new Position(5, 0), new Position(5, 13)))], $result); + Loop::run(function () { + // function test_function() + // Get references for test_function + $result = yield $this->textDocument->references( + new ReferenceContext, + new TextDocumentIdentifier('global_symbols'), + new Position(78, 16) + ); + $this->assertEquals([new Location('global_fallback', new Range(new Position(5, 0), new Position(5, 13)))], $result); + }); } } diff --git a/tests/Server/TextDocument/References/GlobalTest.php b/tests/Server/TextDocument/References/GlobalTest.php index d2550c8a..e18d17f1 100644 --- a/tests/Server/TextDocument/References/GlobalTest.php +++ b/tests/Server/TextDocument/References/GlobalTest.php @@ -1,8 +1,9 @@ getDefinitionLocation('TestClass'); - $result = $this->textDocument->references( - new ReferenceContext, - new TextDocumentIdentifier($definition->uri), - $definition->range->start - )->wait(); - $this->assertEquals($this->getReferenceLocations('TestClass'), $result); + Loop::run(function () { + // class TestClass implements TestInterface + // Get references for TestClass + $definition = $this->getDefinitionLocation('TestClass'); + $result = yield $this->textDocument->references( + new ReferenceContext, + new TextDocumentIdentifier($definition->uri), + $definition->range->start + ); + $this->assertEquals($this->getReferenceLocations('TestClass'), $result); + }); } public function testReferencesForClassConstants() { - // const TEST_CLASS_CONST = 123; - // Get references for TEST_CLASS_CONST - $definition = $this->getDefinitionLocation('TestClass::TEST_CLASS_CONST'); - $result = $this->textDocument->references( - new ReferenceContext, - new TextDocumentIdentifier($definition->uri), - $definition->range->start - )->wait(); - $this->assertEquals($this->getReferenceLocations('TestClass::TEST_CLASS_CONST'), $result); + Loop::run(function () { + // const TEST_CLASS_CONST = 123; + // Get references for TEST_CLASS_CONST + $definition = $this->getDefinitionLocation('TestClass::TEST_CLASS_CONST'); + $result = yield $this->textDocument->references( + new ReferenceContext, + new TextDocumentIdentifier($definition->uri), + $definition->range->start + ); + $this->assertEquals($this->getReferenceLocations('TestClass::TEST_CLASS_CONST'), $result); + }); } public function testReferencesForConstants() { - // const TEST_CONST = 123; - // Get references for TEST_CONST - $definition = $this->getDefinitionLocation('TEST_CONST'); - $result = $this->textDocument->references( - new ReferenceContext, - new TextDocumentIdentifier($definition->uri), - $definition->range->start - )->wait(); - $this->assertEquals($this->getReferenceLocations('TEST_CONST'), $result); + Loop::run(function () { + // const TEST_CONST = 123; + // Get references for TEST_CONST + $definition = $this->getDefinitionLocation('TEST_CONST'); + $result = yield $this->textDocument->references( + new ReferenceContext, + new TextDocumentIdentifier($definition->uri), + $definition->range->start + ); + $this->assertEquals($this->getReferenceLocations('TEST_CONST'), $result); + }); } public function testReferencesForStaticMethods() { - // public static function staticTestMethod() - // Get references for staticTestMethod - $definition = $this->getDefinitionLocation('TestClass::staticTestMethod()'); - $result = $this->textDocument->references( - new ReferenceContext, - new TextDocumentIdentifier($definition->uri), - $definition->range->start - )->wait(); - $this->assertEquals($this->getReferenceLocations('TestClass::staticTestMethod()'), $result); + Loop::run(function () { + // public static function staticTestMethod() + // Get references for staticTestMethod + $definition = $this->getDefinitionLocation('TestClass::staticTestMethod()'); + $result = yield $this->textDocument->references( + new ReferenceContext, + new TextDocumentIdentifier($definition->uri), + $definition->range->start + ); + $this->assertEquals($this->getReferenceLocations('TestClass::staticTestMethod()'), $result); + }); } public function testReferencesForStaticProperties() { - // public static $staticTestProperty; - // Get references for $staticTestProperty - $definition = $this->getDefinitionLocation('TestClass::staticTestProperty'); - $result = $this->textDocument->references( - new ReferenceContext, - new TextDocumentIdentifier($definition->uri), - $definition->range->start - )->wait(); - $this->assertEquals($this->getReferenceLocations('TestClass::staticTestProperty'), $result); + Loop::run(function () { + // public static $staticTestProperty; + // Get references for $staticTestProperty + $definition = $this->getDefinitionLocation('TestClass::staticTestProperty'); + $result = yield $this->textDocument->references( + new ReferenceContext, + new TextDocumentIdentifier($definition->uri), + $definition->range->start + ); + $this->assertEquals($this->getReferenceLocations('TestClass::staticTestProperty'), $result); + }); } public function testReferencesForMethods() { - // public function testMethod($testParameter) - // Get references for testMethod - $definition = $this->getDefinitionLocation('TestClass::testMethod()'); - $result = $this->textDocument->references( - new ReferenceContext, - new TextDocumentIdentifier($definition->uri), - $definition->range->start - )->wait(); - $this->assertEquals($this->getReferenceLocations('TestClass::testMethod()'), $result); + Loop::run(function () { + // public function testMethod($testParameter) + // Get references for testMethod + $definition = $this->getDefinitionLocation('TestClass::testMethod()'); + $result = yield $this->textDocument->references( + new ReferenceContext, + new TextDocumentIdentifier($definition->uri), + $definition->range->start + ); + $this->assertEquals($this->getReferenceLocations('TestClass::testMethod()'), $result); + }); } public function testReferencesForProperties() { - // public $testProperty; - // Get references for testProperty - $definition = $this->getDefinitionLocation('TestClass::testProperty'); - $result = $this->textDocument->references( - new ReferenceContext, - new TextDocumentIdentifier($definition->uri), - $definition->range->start - )->wait(); - $this->assertEquals($this->getReferenceLocations('TestClass::testProperty'), $result); + Loop::run(function () { + // public $testProperty; + // Get references for testProperty + $definition = $this->getDefinitionLocation('TestClass::testProperty'); + $result = yield $this->textDocument->references( + new ReferenceContext, + new TextDocumentIdentifier($definition->uri), + $definition->range->start + ); + $this->assertEquals($this->getReferenceLocations('TestClass::testProperty'), $result); + }); } public function testReferencesForVariables() { - // $var = 123; - // Get definition for $var - $uri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/references.php')); - $result = $this->textDocument->references( - new ReferenceContext, - new TextDocumentIdentifier($uri), - new Position(12, 3) - )->wait(); - $this->assertEquals([ - new Location($uri, new Range(new Position(12, 0), new Position(12, 4))), - new Location($uri, new Range(new Position(13, 5), new Position(13, 9))), - new Location($uri, new Range(new Position(26, 9), new Position(26, 13))) - ], $result); + Loop::run(function () { + // $var = 123; + // Get definition for $var + $uri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/references.php')); + $result = yield $this->textDocument->references( + new ReferenceContext, + new TextDocumentIdentifier($uri), + new Position(12, 3) + ); + $this->assertEquals([ + new Location($uri, new Range(new Position(12, 0), new Position(12, 4))), + new Location($uri, new Range(new Position(13, 5), new Position(13, 9))), + new Location($uri, new Range(new Position(26, 9), new Position(26, 13))) + ], $result); + }); } public function testReferencesForFunctionParams() { - // function whatever(TestClass $param): TestClass - // Get references for $param - $uri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/references.php')); - $result = $this->textDocument->references( - new ReferenceContext, - new TextDocumentIdentifier($uri), - new Position(21, 32) - )->wait(); - $this->assertEquals([new Location($uri, new Range(new Position(22, 9), new Position(22, 15)))], $result); + Loop::run(function () { + // function whatever(TestClass $param): TestClass + // Get references for $param + $uri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/references.php')); + $result = yield $this->textDocument->references( + new ReferenceContext, + new TextDocumentIdentifier($uri), + new Position(21, 32) + ); + $this->assertEquals([new Location($uri, new Range(new Position(22, 9), new Position(22, 15)))], $result); + }); } public function testReferencesForFunctions() { - // function test_function() - // Get references for test_function - $referencesUri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/references.php')); - $symbolsUri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/symbols.php')); - $result = $this->textDocument->references( - new ReferenceContext, - new TextDocumentIdentifier($symbolsUri), - new Position(78, 16) - )->wait(); - $this->assertEquals([ - new Location($referencesUri, new Range(new Position(10, 0), new Position(10, 13))), - new Location($referencesUri, new Range(new Position(31, 13), new Position(31, 40))) - ], $result); + Loop::run(function () { + // function test_function() + // Get references for test_function + $referencesUri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/references.php')); + $symbolsUri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/symbols.php')); + $result = yield $this->textDocument->references( + new ReferenceContext, + new TextDocumentIdentifier($symbolsUri), + new Position(78, 16) + ); + $this->assertEquals([ + new Location($referencesUri, new Range(new Position(10, 0), new Position(10, 13))), + new Location($referencesUri, new Range(new Position(31, 13), new Position(31, 40))) + ], $result); + }); } public function testReferencesForReference() { - // $obj = new TestClass(); - // Get references for TestClass - $reference = $this->getReferenceLocations('TestClass')[1]; - $result = $this->textDocument->references( - new ReferenceContext, - new TextDocumentIdentifier($reference->uri), - $reference->range->start - )->wait(); - $this->assertEquals($this->getReferenceLocations('TestClass'), $result); + Loop::run(function () { + // $obj = new TestClass(); + // Get references for TestClass + $reference = $this->getReferenceLocations('TestClass')[1]; + $result = yield $this->textDocument->references( + new ReferenceContext, + new TextDocumentIdentifier($reference->uri), + $reference->range->start + ); + $this->assertEquals($this->getReferenceLocations('TestClass'), $result); + }); } public function testReferencesForUnusedClass() { - // class UnusedClass - // Get references for UnusedClass - $symbolsUri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/global_symbols.php')); - $result = $this->textDocument->references( - new ReferenceContext, - new TextDocumentIdentifier($symbolsUri), - new Position(111, 10) - )->wait(); - $this->assertEquals([], $result); + Loop::run(function () { + // class UnusedClass + // Get references for UnusedClass + $symbolsUri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/global_symbols.php')); + $result = yield $this->textDocument->references( + new ReferenceContext, + new TextDocumentIdentifier($symbolsUri), + new Position(111, 10) + ); + $this->assertEquals([], $result); + }); } public function testReferencesForUnusedProperty() { - // public $unusedProperty - // Get references for unusedProperty - $symbolsUri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/global_symbols.php')); - $result = $this->textDocument->references( - new ReferenceContext, - new TextDocumentIdentifier($symbolsUri), - new Position(113, 18) - )->wait(); - $this->assertEquals([], $result); + Loop::run(function () { + // public $unusedProperty + // Get references for unusedProperty + $symbolsUri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/global_symbols.php')); + $result = yield $this->textDocument->references( + new ReferenceContext, + new TextDocumentIdentifier($symbolsUri), + new Position(113, 18) + ); + $this->assertEquals([], $result); + }); } public function testReferencesForUnusedMethod() { - // public function unusedMethod() - // Get references for unusedMethod - $symbolsUri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/global_symbols.php')); - $result = $this->textDocument->references( - new ReferenceContext, - new TextDocumentIdentifier($symbolsUri), - new Position(115, 26) - )->wait(); - $this->assertEquals([], $result); + Loop::run(function () { + // public function unusedMethod() + // Get references for unusedMethod + $symbolsUri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/global_symbols.php')); + $result = yield $this->textDocument->references( + new ReferenceContext, + new TextDocumentIdentifier($symbolsUri), + new Position(115, 26) + ); + $this->assertEquals([], $result); + }); } } diff --git a/tests/Server/TextDocument/References/NamespacedTest.php b/tests/Server/TextDocument/References/NamespacedTest.php index b2bb9bbe..723aa79a 100644 --- a/tests/Server/TextDocument/References/NamespacedTest.php +++ b/tests/Server/TextDocument/References/NamespacedTest.php @@ -1,8 +1,9 @@ textDocument->references( - new ReferenceContext, - new TextDocumentIdentifier($definition->uri), - $definition->range->end - )->wait(); - $this->assertEquals(parent::getReferenceLocations('TestNamespace'), $result); + Loop::run(function () { + // namespace TestNamespace; + // Get references for TestNamespace + $definition = parent::getDefinitionLocation('TestNamespace'); + $result = yield $this->textDocument->references( + new ReferenceContext, + new TextDocumentIdentifier($definition->uri), + $definition->range->end + ); + $this->assertEquals(parent::getReferenceLocations('TestNamespace'), $result); + }); } } diff --git a/tests/Server/TextDocument/SignatureHelpTest.php b/tests/Server/TextDocument/SignatureHelpTest.php index 83dcb2c6..8939c2e9 100644 --- a/tests/Server/TextDocument/SignatureHelpTest.php +++ b/tests/Server/TextDocument/SignatureHelpTest.php @@ -1,8 +1,9 @@ loader->open($callsUri, file_get_contents($callsUri)); - $signatureHelp = $this->textDocument->signatureHelp( - new TextDocumentIdentifier($callsUri), - $position - )->wait(); - $this->assertEquals($expectedSignature, $signatureHelp); + Loop::run(function () use ($position, $expectedSignature) { + $callsUri = pathToUri(__DIR__ . '/../../../fixtures/signature_help/calls.php'); + $this->loader->open($callsUri, file_get_contents($callsUri)); + $signatureHelp = yield $this->textDocument->signatureHelp( + new TextDocumentIdentifier($callsUri), + $position + ); + $this->assertEquals($expectedSignature, $signatureHelp); + }); } public function signatureHelpProvider(): array diff --git a/tests/Server/Workspace/DidChangeWatchedFilesTest.php b/tests/Server/Workspace/DidChangeWatchedFilesTest.php index 1f57acdc..9aa7a353 100644 --- a/tests/Server/Workspace/DidChangeWatchedFilesTest.php +++ b/tests/Server/Workspace/DidChangeWatchedFilesTest.php @@ -1,42 +1,46 @@ on('message', function (Message $message) use ($fileEvent, &$isDiagnosticsCleared) { - if ($message->body->method === "textDocument/publishDiagnostics") { - $this->assertEquals($message->body->params->uri, $fileEvent->uri); - $this->assertEquals($message->body->params->diagnostics, []); - $isDiagnosticsCleared = true; - } - }); + $isDiagnosticsCleared = false; + $deferred = new Deferred(); + $writer->addListener('message', function (MessageEvent $messageEvent) use ($deferred, $fileEvent, &$isDiagnosticsCleared) { + $message = $messageEvent->getMessage(); + if ($message->body->method === "textDocument/publishDiagnostics") { + $this->assertEquals($message->body->params->uri, $fileEvent->uri); + $this->assertEquals($message->body->params->diagnostics, []); + $deferred->resolve(true); + } + }); - $workspace->didChangeWatchedFiles([$fileEvent]); - Loop\tick(true); + $workspace->didChangeWatchedFiles([$fileEvent]); - $this->assertTrue($isDiagnosticsCleared, "Deleting file should clear all diagnostics."); + $this->assertTrue(yield $deferred->promise(), "Deleting file should clear all diagnostics."); + }); } } diff --git a/tests/Server/Workspace/SymbolTest.php b/tests/Server/Workspace/SymbolTest.php index 74fc92e0..5b2790b0 100644 --- a/tests/Server/Workspace/SymbolTest.php +++ b/tests/Server/Workspace/SymbolTest.php @@ -1,8 +1,9 @@ workspace->symbol('')->wait(); - $referencesUri = pathToUri(realpath(__DIR__ . '/../../../fixtures/references.php')); + Loop::run(function () { + // Request symbols + $result = yield $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'), - new SymbolInformation('TEST_CLASS_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestNamespace\\TestClass::TEST_CLASS_CONST'), 'TestNamespace\\TestClass'), - new SymbolInformation('staticTestProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestNamespace\\TestClass::staticTestProperty'), 'TestNamespace\\TestClass'), - new SymbolInformation('testProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestNamespace\\TestClass::testProperty'), 'TestNamespace\\TestClass'), - new SymbolInformation('staticTestMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestNamespace\\TestClass::staticTestMethod()'), 'TestNamespace\\TestClass'), - new SymbolInformation('testMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestNamespace\\TestClass::testMethod()'), 'TestNamespace\\TestClass'), - new SymbolInformation('TestTrait', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\TestTrait'), 'TestNamespace'), - new SymbolInformation('TestInterface', SymbolKind::INTERFACE, $this->getDefinitionLocation('TestNamespace\\TestInterface'), 'TestNamespace'), - new SymbolInformation('test_function', SymbolKind::FUNCTION, $this->getDefinitionLocation('TestNamespace\\test_function()'), 'TestNamespace'), - new SymbolInformation('ChildClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\ChildClass'), 'TestNamespace'), - new SymbolInformation('Example', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\Example'), 'TestNamespace'), - new SymbolInformation('__construct', SymbolKind::CONSTRUCTOR, $this->getDefinitionLocation('TestNamespace\\Example::__construct'), 'TestNamespace\\Example'), - new SymbolInformation('__destruct', SymbolKind::CONSTRUCTOR, $this->getDefinitionLocation('TestNamespace\\Example::__destruct'), 'TestNamespace\\Example'), - new SymbolInformation('TestNamespace\\InnerNamespace', SymbolKind::NAMESPACE, $this->getDefinitionLocation('TestNamespace\\InnerNamespace'), 'TestNamespace'), - new SymbolInformation('InnerClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\InnerNamespace\\InnerClass'), 'TestNamespace\\InnerNamespace'), - new SymbolInformation('whatever', SymbolKind::FUNCTION, $this->getDefinitionLocation('TestNamespace\\whatever()'), 'TestNamespace'), - // Global - new SymbolInformation('TEST_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TEST_CONST'), ''), - new SymbolInformation('TestClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestClass'), ''), - new SymbolInformation('TEST_CLASS_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestClass::TEST_CLASS_CONST'), 'TestClass'), - new SymbolInformation('staticTestProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestClass::staticTestProperty'), 'TestClass'), - new SymbolInformation('testProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestClass::testProperty'), 'TestClass'), - new SymbolInformation('staticTestMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestClass::staticTestMethod()'), 'TestClass'), - new SymbolInformation('testMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestClass::testMethod()'), 'TestClass'), - 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('ChildClass', SymbolKind::CLASS_, $this->getDefinitionLocation('ChildClass'), ''), - new SymbolInformation('TEST_DEFINE_CONSTANT', SymbolKind::CONSTANT, $this->getDefinitionLocation('TEST_DEFINE_CONSTANT'), ''), - new SymbolInformation('UnusedClass', SymbolKind::CLASS_, $this->getDefinitionLocation('UnusedClass'), ''), - new SymbolInformation('unusedProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('UnusedClass::unusedProperty'), 'UnusedClass'), - new SymbolInformation('unusedMethod', SymbolKind::METHOD, $this->getDefinitionLocation('UnusedClass::unusedMethod'), 'UnusedClass'), - new SymbolInformation('whatever', SymbolKind::FUNCTION, $this->getDefinitionLocation('whatever()'), ''), + // @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'), + new SymbolInformation('TEST_CLASS_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestNamespace\\TestClass::TEST_CLASS_CONST'), 'TestNamespace\\TestClass'), + new SymbolInformation('staticTestProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestNamespace\\TestClass::staticTestProperty'), 'TestNamespace\\TestClass'), + new SymbolInformation('testProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestNamespace\\TestClass::testProperty'), 'TestNamespace\\TestClass'), + new SymbolInformation('staticTestMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestNamespace\\TestClass::staticTestMethod()'), 'TestNamespace\\TestClass'), + new SymbolInformation('testMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestNamespace\\TestClass::testMethod()'), 'TestNamespace\\TestClass'), + new SymbolInformation('TestTrait', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\TestTrait'), 'TestNamespace'), + new SymbolInformation('TestInterface', SymbolKind::INTERFACE, $this->getDefinitionLocation('TestNamespace\\TestInterface'), 'TestNamespace'), + new SymbolInformation('test_function', SymbolKind::FUNCTION, $this->getDefinitionLocation('TestNamespace\\test_function()'), 'TestNamespace'), + new SymbolInformation('ChildClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\ChildClass'), 'TestNamespace'), + new SymbolInformation('Example', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\Example'), 'TestNamespace'), + new SymbolInformation('__construct', SymbolKind::CONSTRUCTOR, $this->getDefinitionLocation('TestNamespace\\Example::__construct'), 'TestNamespace\\Example'), + new SymbolInformation('__destruct', SymbolKind::CONSTRUCTOR, $this->getDefinitionLocation('TestNamespace\\Example::__destruct'), 'TestNamespace\\Example'), + new SymbolInformation('TestNamespace\\InnerNamespace', SymbolKind::NAMESPACE, $this->getDefinitionLocation('TestNamespace\\InnerNamespace'), 'TestNamespace'), + new SymbolInformation('InnerClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\InnerNamespace\\InnerClass'), 'TestNamespace\\InnerNamespace'), + new SymbolInformation('whatever', SymbolKind::FUNCTION, $this->getDefinitionLocation('TestNamespace\\whatever()'), 'TestNamespace'), + // Global + new SymbolInformation('TEST_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TEST_CONST'), ''), + new SymbolInformation('TestClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestClass'), ''), + new SymbolInformation('TEST_CLASS_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestClass::TEST_CLASS_CONST'), 'TestClass'), + new SymbolInformation('staticTestProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestClass::staticTestProperty'), 'TestClass'), + new SymbolInformation('testProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestClass::testProperty'), 'TestClass'), + new SymbolInformation('staticTestMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestClass::staticTestMethod()'), 'TestClass'), + new SymbolInformation('testMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestClass::testMethod()'), 'TestClass'), + 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('ChildClass', SymbolKind::CLASS_, $this->getDefinitionLocation('ChildClass'), ''), + new SymbolInformation('TEST_DEFINE_CONSTANT', SymbolKind::CONSTANT, $this->getDefinitionLocation('TEST_DEFINE_CONSTANT'), ''), + new SymbolInformation('UnusedClass', SymbolKind::CLASS_, $this->getDefinitionLocation('UnusedClass'), ''), + new SymbolInformation('unusedProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('UnusedClass::unusedProperty'), 'UnusedClass'), + new SymbolInformation('unusedMethod', SymbolKind::METHOD, $this->getDefinitionLocation('UnusedClass::unusedMethod'), 'UnusedClass'), + new SymbolInformation('whatever', SymbolKind::FUNCTION, $this->getDefinitionLocation('whatever()'), ''), - new SymbolInformation('SecondTestNamespace', SymbolKind::NAMESPACE, $this->getDefinitionLocation('SecondTestNamespace'), ''), - ], $result); - // @codingStandardsIgnoreEnd + new SymbolInformation('SecondTestNamespace', SymbolKind::NAMESPACE, $this->getDefinitionLocation('SecondTestNamespace'), ''), + ], $result); + // @codingStandardsIgnoreEnd + }); } public function testQueryFiltersResults() { - // Request symbols - $result = $this->workspace->symbol('testmethod')->wait(); - // @codingStandardsIgnoreStart - $this->assertEquals([ - new SymbolInformation('staticTestMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestNamespace\\TestClass::staticTestMethod()'), 'TestNamespace\\TestClass'), - new SymbolInformation('testMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestNamespace\\TestClass::testMethod()'), 'TestNamespace\\TestClass'), - new SymbolInformation('staticTestMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestClass::staticTestMethod()'), 'TestClass'), - new SymbolInformation('testMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestClass::testMethod()'), 'TestClass') - ], $result); - // @codingStandardsIgnoreEnd + Loop::run(function () { + // Request symbols + $result = yield $this->workspace->symbol('testmethod'); + // @codingStandardsIgnoreStart + $this->assertEquals([ + new SymbolInformation('staticTestMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestNamespace\\TestClass::staticTestMethod()'), 'TestNamespace\\TestClass'), + new SymbolInformation('testMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestNamespace\\TestClass::testMethod()'), 'TestNamespace\\TestClass'), + new SymbolInformation('staticTestMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestClass::staticTestMethod()'), 'TestClass'), + new SymbolInformation('testMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestClass::testMethod()'), 'TestClass') + ], $result); + // @codingStandardsIgnoreEnd + }); } } diff --git a/tests/Validation/cases/functionUse2.php b/tests/Validation/cases/functionUse2.php index 593e6ac9..d06de2c9 100644 --- a/tests/Validation/cases/functionUse2.php +++ b/tests/Validation/cases/functionUse2.php @@ -1,4 +1,4 @@