diff --git a/composer.json b/composer.json index 6d9159b..2afb468 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,11 @@ "sabre/event": "^5.0", "felixfbecker/advanced-json-rpc": "^2.0", "squizlabs/php_codesniffer" : "^2.7", - "symfony/debug": "^3.1" + "symfony/debug": "^3.1", + "netresearch/jsonmapper": "^1.0", + "webmozart/path-util": "^2.3", + "webmozart/glob": "^4.1", + "sabre/uri": "^2.0" }, "minimum-stability": "dev", "prefer-stable": true, diff --git a/fixtures/recursive/a.txt b/fixtures/recursive/a.txt deleted file mode 100644 index 8c7e5a6..0000000 --- a/fixtures/recursive/a.txt +++ /dev/null @@ -1 +0,0 @@ -A \ No newline at end of file diff --git a/fixtures/recursive/search/b.txt b/fixtures/recursive/search/b.txt deleted file mode 100644 index 7371f47..0000000 --- a/fixtures/recursive/search/b.txt +++ /dev/null @@ -1 +0,0 @@ -B \ No newline at end of file diff --git a/fixtures/recursive/search/here/c.txt b/fixtures/recursive/search/here/c.txt deleted file mode 100644 index d5274b3..0000000 --- a/fixtures/recursive/search/here/c.txt +++ /dev/null @@ -1 +0,0 @@ -Peeakboo! \ No newline at end of file diff --git a/src/Client/TextDocument.php b/src/Client/TextDocument.php index d5314e4..176c4fd 100644 --- a/src/Client/TextDocument.php +++ b/src/Client/TextDocument.php @@ -4,8 +4,9 @@ declare(strict_types = 1); namespace LanguageServer\Client; use LanguageServer\ClientHandler; -use LanguageServer\Protocol\Message; +use LanguageServer\Protocol\{Message, TextDocumentItem, TextDocumentIdentifier}; use Sabre\Event\Promise; +use JsonMapper; /** * Provides method handlers for all textDocument/* methods @@ -17,9 +18,15 @@ class TextDocument */ private $handler; - public function __construct(ClientHandler $handler) + /** + * @var JsonMapper + */ + private $mapper; + + public function __construct(ClientHandler $handler, JsonMapper $mapper) { $this->handler = $handler; + $this->mapper = $mapper; } /** @@ -36,4 +43,21 @@ class TextDocument 'diagnostics' => $diagnostics ]); } + + /** + * The content request is sent from a server to a client + * to request the current content of a text document identified by the URI + * + * @param TextDocumentIdentifier $textDocument The document to get the content for + * @return Promise The document's current content + */ + public function xcontent(TextDocumentIdentifier $textDocument): Promise + { + return $this->handler->request( + 'textDocument/xcontent', + ['textDocument' => $textDocument] + )->then(function ($result) { + return $this->mapper->map($result, new TextDocumentItem); + }); + } } diff --git a/src/Client/Workspace.php b/src/Client/Workspace.php new file mode 100644 index 0000000..901e386 --- /dev/null +++ b/src/Client/Workspace.php @@ -0,0 +1,47 @@ +handler = $handler; + $this->mapper = $mapper; + } + + /** + * Returns a list of all files in a directory + * + * @param string $base The base directory (defaults to the workspace) + * @return Promise Array of documents + */ + public function xfiles(string $base = null): Promise + { + return $this->handler->request( + 'workspace/xfiles', + ['base' => $base] + )->then(function (array $textDocuments) { + return $this->mapper->mapArray($textDocuments, [], TextDocumentIdentifier::class); + }); + } +} diff --git a/src/LanguageClient.php b/src/LanguageClient.php index 1f3c42a..d21a9aa 100644 --- a/src/LanguageClient.php +++ b/src/LanguageClient.php @@ -3,6 +3,8 @@ declare(strict_types = 1); namespace LanguageServer; +use JsonMapper; + class LanguageClient { /** @@ -19,11 +21,20 @@ class LanguageClient */ public $window; + /** + * Handles workspace/* methods + * + * @var Client\Workspace + */ + public $workspace; + public function __construct(ProtocolReader $reader, ProtocolWriter $writer) { $handler = new ClientHandler($reader, $writer); + $mapper = new JsonMapper; - $this->textDocument = new Client\TextDocument($handler); + $this->textDocument = new Client\TextDocument($handler, $mapper); $this->window = new Client\Window($handler); + $this->workspace = new Client\Workspace($handler, $mapper); } } diff --git a/src/LanguageServer.php b/src/LanguageServer.php index ddcddc7..9a89066 100644 --- a/src/LanguageServer.php +++ b/src/LanguageServer.php @@ -10,12 +10,18 @@ use LanguageServer\Protocol\{ Message, MessageType, InitializeResult, - SymbolInformation + SymbolInformation, + TextDocumentIdentifier }; use AdvancedJsonRpc; -use Sabre\Event\Loop; +use Sabre\Event\{Loop, Promise}; +use function Sabre\Event\coroutine; use Exception; use Throwable; +use Webmozart\Glob\Iterator\GlobIterator; +use Webmozart\Glob\Glob; +use Webmozart\PathUtil\Path; +use Sabre\Uri; class LanguageServer extends AdvancedJsonRpc\Dispatcher { @@ -38,6 +44,11 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher public $completionItem; public $codeLens; + /** + * ClientCapabilities + */ + private $clientCapabilities; + private $protocolReader; private $protocolWriter; private $client; @@ -55,40 +66,42 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher parent::__construct($this, '/'); $this->protocolReader = $reader; $this->protocolReader->on('message', function (Message $msg) { - // Ignore responses, this is the handler for requests and notifications - if (AdvancedJsonRpc\Response::isResponse($msg->body)) { - return; - } - $result = null; - $error = null; - try { - // Invoke the method handler to get a result - $result = $this->dispatch($msg->body); - } catch (AdvancedJsonRpc\Error $e) { - // If a ResponseError is thrown, send it back in the Response - $error = $e; - } catch (Throwable $e) { - // If an unexpected error occured, send back an INTERNAL_ERROR error response - $error = new AdvancedJsonRpc\Error($e->getMessage(), AdvancedJsonRpc\ErrorCode::INTERNAL_ERROR, null, $e); - } - // Only send a Response for a Request - // Notifications do not send Responses - if (AdvancedJsonRpc\Request::isRequest($msg->body)) { - if ($error !== null) { - $responseBody = new AdvancedJsonRpc\ErrorResponse($msg->body->id, $error); - } else { - $responseBody = new AdvancedJsonRpc\SuccessResponse($msg->body->id, $result); + coroutine(function () use ($msg) { + // Ignore responses, this is the handler for requests and notifications + if (AdvancedJsonRpc\Response::isResponse($msg->body)) { + return; } - $this->protocolWriter->write(new Message($responseBody)); - } + $result = null; + $error = null; + try { + // Invoke the method handler to get a result + $result = yield $this->dispatch($msg->body); + } catch (AdvancedJsonRpc\Error $e) { + // If a ResponseError is thrown, send it back in the Response + $error = $e; + } catch (Throwable $e) { + // If an unexpected error occured, send back an INTERNAL_ERROR error response + $error = new AdvancedJsonRpc\Error( + $e->getMessage(), + AdvancedJsonRpc\ErrorCode::INTERNAL_ERROR, + null, + $e + ); + } + // Only send a Response for a Request + // Notifications do not send Responses + if (AdvancedJsonRpc\Request::isRequest($msg->body)) { + if ($error !== null) { + $responseBody = new AdvancedJsonRpc\ErrorResponse($msg->body->id, $error); + } else { + $responseBody = new AdvancedJsonRpc\SuccessResponse($msg->body->id, $result); + } + $this->protocolWriter->write(new Message($responseBody)); + } + })->otherwise('\\LanguageServer\\crash'); }); $this->protocolWriter = $writer; $this->client = new LanguageClient($reader, $writer); - - $this->project = new Project($this->client); - - $this->textDocument = new Server\TextDocument($this->project, $this->client); - $this->workspace = new Server\Workspace($this->project, $this->client); } /** @@ -102,10 +115,14 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher public function initialize(int $processId, ClientCapabilities $capabilities, string $rootPath = null): InitializeResult { $this->rootPath = $rootPath; + $this->clientCapabilities = $capabilities; + $this->project = new Project($this->client, $capabilities); + $this->textDocument = new Server\TextDocument($this->project, $this->client); + $this->workspace = new Server\Workspace($this->project, $this->client); // start building project index if ($rootPath !== null) { - $this->indexProject(); + $this->indexProject()->otherwise('\\LanguageServer\\crash'); } $serverCapabilities = new ServerCapabilities(); @@ -136,6 +153,7 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher */ public function shutdown() { + unset($this->project); } /** @@ -151,42 +169,71 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher /** * Parses workspace files, one at a time. * - * @return void + * @return Promise */ - private function indexProject() + private function indexProject(): Promise { - $fileList = findFilesRecursive($this->rootPath, '/^.+\.php$/i'); - $numTotalFiles = count($fileList); + return coroutine(function () { + $textDocuments = yield $this->findPhpFiles(); + $count = count($textDocuments); - $startTime = microtime(true); - $fileNum = 0; + $startTime = microtime(true); - $processFile = function () use (&$fileList, &$fileNum, &$processFile, $numTotalFiles, $startTime) { - if ($fileNum < $numTotalFiles) { - $file = $fileList[$fileNum]; - $uri = pathToUri($file); - $fileNum++; - $shortName = substr($file, strlen($this->rootPath) + 1); - - if (filesize($file) > 500000) { - $this->client->window->logMessage(MessageType::INFO, "Not parsing $shortName because it exceeds size limit of 0.5MB"); - } else { - $this->client->window->logMessage(MessageType::INFO, "Parsing file $fileNum/$numTotalFiles: $shortName."); + yield Promise\all(array_map(function ($textDocument, $i) use ($count) { + return coroutine(function () use ($textDocument, $i, $count) { + // Give LS to the chance to handle requests while indexing + yield timeout(); + $this->client->window->logMessage( + MessageType::INFO, + "Parsing file $i/$count: {$textDocument->uri}" + ); try { - $this->project->loadDocument($uri); + yield $this->project->loadDocument($textDocument->uri); } catch (Exception $e) { - $this->client->window->logMessage(MessageType::ERROR, "Error parsing file $shortName: " . (string)$e); + $this->client->window->logMessage( + MessageType::ERROR, + "Error parsing file {$textDocument->uri}: " . (string)$e + ); + } + }); + }, $textDocuments, array_keys($textDocuments))); + + $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." + ); + }); + } + + /** + * Returns all PHP files in the workspace. + * If the client does not support workspace/files, it falls back to searching the file system directly. + * + * @return Promise + */ + private function findPhpFiles(): Promise + { + return coroutine(function () { + $textDocuments = []; + $pattern = Path::makeAbsolute('**/*.php', $this->rootPath); + if ($this->clientCapabilities->xfilesProvider) { + // Use xfiles request + foreach (yield $this->client->workspace->xfiles() as $textDocument) { + $path = Uri\parse($textDocument->uri)['path']; + if (Glob::match($path, $pattern)) { + $textDocuments[] = $textDocument; } } - - Loop\setTimeout($processFile, 0); } else { - $duration = (int)(microtime(true) - $startTime); - $mem = (int)(memory_get_usage(true) / (1024 * 1024)); - $this->client->window->logMessage(MessageType::INFO, "All $numTotalFiles PHP files parsed in $duration seconds. $mem MiB allocated."); + // Use the file system + foreach (new GlobIterator($pattern) as $path) { + $textDocuments[] = new TextDocumentIdentifier(pathToUri($path)); + yield timeout(); + } } - }; - - Loop\setTimeout($processFile, 0); + return $textDocuments; + }); } } diff --git a/src/PhpDocument.php b/src/PhpDocument.php index 5033959..75badca 100644 --- a/src/PhpDocument.php +++ b/src/PhpDocument.php @@ -17,6 +17,8 @@ use PhpParser\{Error, ErrorHandler, Node, NodeTraverser}; use PhpParser\NodeVisitor\NameResolver; use phpDocumentor\Reflection\DocBlockFactory; use function LanguageServer\Fqn\{getDefinedFqn, getVariableDefinition, getReferencedFqn}; +use Sabre\Event\Promise; +use function Sabre\Event\coroutine; class PhpDocument { @@ -314,34 +316,36 @@ class PhpDocument * The definition node MAY be in another document, check the ownerDocument attribute * * @param Node $node - * @return Node|null + * @return Promise */ - public function getDefinitionByNode(Node $node) + public function getDefinitionByNode(Node $node): Promise { - // 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\Expr\Variable) { - return getVariableDefinition($node); - } - $fqn = getReferencedFqn($node); - if (!isset($fqn)) { - return null; - } - $document = $this->project->getDefinitionDocument($fqn); - if (!isset($document)) { - // If the node is a function or constant, it could be namespaced, but PHP falls back to global - // http://php.net/manual/en/language.namespaces.fallback.php - $parent = $node->getAttribute('parentNode'); - if ($parent instanceof Node\Expr\ConstFetch || $parent instanceof Node\Expr\FuncCall) { - $parts = explode('\\', $fqn); - $fqn = end($parts); - $document = $this->project->getDefinitionDocument($fqn); + return coroutine(function () use ($node) { + // 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\Expr\Variable) { + return getVariableDefinition($node); } - } - if (!isset($document)) { - return null; - } - return $document->getDefinitionByFqn($fqn); + $fqn = getReferencedFqn($node); + if (!isset($fqn)) { + return null; + } + $document = yield $this->project->getDefinitionDocument($fqn); + if (!isset($document)) { + // If the node is a function or constant, it could be namespaced, but PHP falls back to global + // http://php.net/manual/en/language.namespaces.fallback.php + $parent = $node->getAttribute('parentNode'); + if ($parent instanceof Node\Expr\ConstFetch || $parent instanceof Node\Expr\FuncCall) { + $parts = explode('\\', $fqn); + $fqn = end($parts); + $document = yield $this->project->getDefinitionDocument($fqn); + } + } + if (!isset($document)) { + return null; + } + return $document->getDefinitionByFqn($fqn); + }); } /** @@ -349,45 +353,47 @@ class PhpDocument * The references node MAY be in other documents, check the ownerDocument attribute * * @param Node $node - * @return Node[] + * @return Promise */ - public function getReferencesByNode(Node $node) + public function getReferencesByNode(Node $node): Promise { - // 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\Expr\Variable || $node instanceof Node\Param) { - if ($node->name instanceof Node\Expr) { - return null; + return coroutine(function () use ($node) { + // 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\Expr\Variable || $node instanceof Node\Param) { + if ($node->name instanceof Node\Expr) { + return null; + } + // Find function/method/closure scope + $n = $node; + while (isset($n) && !($n instanceof Node\FunctionLike)) { + $n = $n->getAttribute('parentNode'); + } + if (!isset($n)) { + $n = $node->getAttribute('ownerDocument'); + } + $traverser = new NodeTraverser; + $refCollector = new VariableReferencesCollector($node->name); + $traverser->addVisitor($refCollector); + $traverser->traverse($n->getStmts()); + return $refCollector->references; } - // Find function/method/closure scope - $n = $node; - while (isset($n) && !($n instanceof Node\FunctionLike)) { - $n = $n->getAttribute('parentNode'); + // Definition with a global FQN + $fqn = getDefinedFqn($node); + if ($fqn === null) { + return []; } - if (!isset($n)) { - $n = $node->getAttribute('ownerDocument'); - } - $traverser = new NodeTraverser; - $refCollector = new VariableReferencesCollector($node->name); - $traverser->addVisitor($refCollector); - $traverser->traverse($n->getStmts()); - return $refCollector->references; - } - // Definition with a global FQN - $fqn = getDefinedFqn($node); - if ($fqn === null) { - return []; - } - $refDocuments = $this->project->getReferenceDocuments($fqn); - $nodes = []; - foreach ($refDocuments as $document) { - $refs = $document->getReferencesByFqn($fqn); - if ($refs !== null) { - foreach ($refs as $ref) { - $nodes[] = $ref; + $refDocuments = yield $this->project->getReferenceDocuments($fqn); + $nodes = []; + foreach ($refDocuments as $document) { + $refs = $document->getReferencesByFqn($fqn); + if ($refs !== null) { + foreach ($refs as $ref) { + $nodes[] = $ref; + } } } - } - return $nodes; + return $nodes; + }); } } diff --git a/src/Project.php b/src/Project.php index 0a5349f..e6c1dbd 100644 --- a/src/Project.php +++ b/src/Project.php @@ -3,8 +3,10 @@ declare(strict_types = 1); namespace LanguageServer; -use LanguageServer\Protocol\SymbolInformation; +use LanguageServer\Protocol\{SymbolInformation, TextDocumentIdentifier, ClientCapabilities}; use phpDocumentor\Reflection\DocBlockFactory; +use Sabre\Event\Promise; +use function Sabre\Event\coroutine; class Project { @@ -51,47 +53,69 @@ class Project */ private $client; - public function __construct(LanguageClient $client) + /** + * The client's capabilities + * + * @var ClientCapabilities + */ + private $clientCapabilities; + + public function __construct(LanguageClient $client, ClientCapabilities $clientCapabilities) { $this->client = $client; - + $this->clientCapabilities = $clientCapabilities; $this->parser = new Parser; $this->docBlockFactory = DocBlockFactory::createInstance(); } /** * Returns the document indicated by uri. - * If the document is not open, tries to read it from disk, but the document is not added the list of open documents. + * Returns null if the document if not loaded. * * @param string $uri - * @return LanguageServer\PhpDocument + * @return PhpDocument|null */ public function getDocument(string $uri) { - if (!isset($this->documents[$uri])) { - return $this->loadDocument($uri); - } else { - return $this->documents[$uri]; - } + return $this->documents[$uri] ?? null; } /** - * Reads a document from disk. + * Returns the document indicated by uri. + * If the document is not open, loads it. + * + * @param string $uri + * @return Promise + */ + public function getOrLoadDocument(string $uri) + { + return isset($this->documents[$uri]) ? Promise\resolve($this->documents[$uri]) : $this->loadDocument($uri); + } + + /** + * Loads the document by doing a textDocument/xcontent request to the client. + * If the client does not support textDocument/xcontent, tries to read the file from the file system. * The document is NOT added to the list of open documents, but definitions are registered. * * @param string $uri - * @return LanguageServer\PhpDocument + * @return Promise */ - public function loadDocument(string $uri) + public function loadDocument(string $uri): Promise { - $content = file_get_contents(uriToPath($uri)); - if (isset($this->documents[$uri])) { - $document = $this->documents[$uri]; - $document->updateContent($content); - } else { - $document = new PhpDocument($uri, $content, $this, $this->client, $this->parser, $this->docBlockFactory); - } - return $document; + return coroutine(function () use ($uri) { + if ($this->clientCapabilities->xcontentProvider) { + $content = (yield $this->client->textDocument->xcontent(new TextDocumentIdentifier($uri)))->text; + } else { + $content = file_get_contents(uriToPath($uri)); + } + if (isset($this->documents[$uri])) { + $document = $this->documents[$uri]; + $document->updateContent($content); + } else { + $document = new PhpDocument($uri, $content, $this, $this->client, $this->parser, $this->docBlockFactory); + } + return $document; + }); } /** @@ -222,14 +246,14 @@ class Project * Returns all documents that reference a symbol * * @param string $fqn The fully qualified name of the symbol - * @return PhpDocument[] + * @return Promise */ - public function getReferenceDocuments(string $fqn) + public function getReferenceDocuments(string $fqn): Promise { if (!isset($this->references[$fqn])) { - return []; + return Promise\resolve([]); } - return array_map([$this, 'getDocument'], $this->references[$fqn]); + return Promise\all(array_map([$this, 'getOrLoadDocument'], $this->references[$fqn])); } /** @@ -258,11 +282,14 @@ class Project * Returns the document where a symbol is defined * * @param string $fqn The fully qualified name of the symbol - * @return PhpDocument|null + * @return Promise */ - public function getDefinitionDocument(string $fqn) + public function getDefinitionDocument(string $fqn): Promise { - return isset($this->symbols[$fqn]) ? $this->getDocument($this->symbols[$fqn]->location->uri) : null; + if (!isset($this->symbols[$fqn])) { + return Promise\resolve(null); + } + return $this->getOrLoadDocument($this->symbols[$fqn]->location->uri); } /** diff --git a/src/Protocol/ClientCapabilities.php b/src/Protocol/ClientCapabilities.php index c85e44b..42137e9 100644 --- a/src/Protocol/ClientCapabilities.php +++ b/src/Protocol/ClientCapabilities.php @@ -4,5 +4,17 @@ namespace LanguageServer\Protocol; class ClientCapabilities { + /** + * The client supports workspace/xfiles requests + * + * @var bool|null + */ + public $xfilesProvider; + /** + * The client supports textDocument/xcontent requests + * + * @var bool|null + */ + public $xcontentProvider; } diff --git a/src/Server/TextDocument.php b/src/Server/TextDocument.php index 188e629..1fc8f27 100644 --- a/src/Server/TextDocument.php +++ b/src/Server/TextDocument.php @@ -3,7 +3,7 @@ declare(strict_types = 1); namespace LanguageServer\Server; -use LanguageServer\{LanguageClient, Project}; +use LanguageServer\{LanguageClient, Project, PhpDocument}; use PhpParser\PrettyPrinter\Standard as PrettyPrinter; use PhpParser\Node; use LanguageServer\Protocol\{ @@ -20,6 +20,8 @@ use LanguageServer\Protocol\{ Hover, MarkedString }; +use Sabre\Event\Promise; +use function Sabre\Event\coroutine; /** * Provides method handlers for all textDocument/* methods @@ -55,11 +57,13 @@ class TextDocument * document. * * @param \LanguageServer\Protocol\TextDocumentIdentifier $textDocument - * @return SymbolInformation[] + * @return Promise */ - public function documentSymbol(TextDocumentIdentifier $textDocument): array + public function documentSymbol(TextDocumentIdentifier $textDocument): Promise { - return array_values($this->project->getDocument($textDocument->uri)->getSymbols()); + return $this->project->getOrLoadDocument($textDocument->uri)->then(function (PhpDocument $document) { + return array_values($document->getSymbols()); + }); } /** @@ -105,11 +109,13 @@ class TextDocument * * @param TextDocumentIdentifier $textDocument The document to format * @param FormattingOptions $options The format options - * @return TextEdit[] + * @return Promise */ public function formatting(TextDocumentIdentifier $textDocument, FormattingOptions $options) { - return $this->project->getDocument($textDocument->uri)->getFormattedText(); + return $this->project->getOrLoadDocument($textDocument->uri)->then(function (PhpDocument $document) { + return $document->getFormattedText(); + }); } /** @@ -117,21 +123,26 @@ class TextDocument * denoted by the given text document position. * * @param ReferenceContext $context - * @return Location[] + * @return Promise */ - public function references(ReferenceContext $context, TextDocumentIdentifier $textDocument, Position $position): array - { - $document = $this->project->getDocument($textDocument->uri); - $node = $document->getNodeAtPosition($position); - if ($node === null) { - return []; - } - $refs = $document->getReferencesByNode($node); - $locations = []; - foreach ($refs as $ref) { - $locations[] = Location::fromNode($ref); - } - return $locations; + public function references( + ReferenceContext $context, + TextDocumentIdentifier $textDocument, + Position $position + ): Promise { + return coroutine(function () use ($textDocument, $position) { + $document = yield $this->project->getOrLoadDocument($textDocument->uri); + $node = $document->getNodeAtPosition($position); + if ($node === null) { + return []; + } + $refs = yield $document->getReferencesByNode($node); + $locations = []; + foreach ($refs as $ref) { + $locations[] = Location::fromNode($ref); + } + return $locations; + }); } /** @@ -140,20 +151,22 @@ class TextDocument * * @param TextDocumentIdentifier $textDocument The text document * @param Position $position The position inside the text document - * @return Location|Location[] + * @return Promise */ - public function definition(TextDocumentIdentifier $textDocument, Position $position) + public function definition(TextDocumentIdentifier $textDocument, Position $position): Promise { - $document = $this->project->getDocument($textDocument->uri); - $node = $document->getNodeAtPosition($position); - if ($node === null) { - return []; - } - $def = $document->getDefinitionByNode($node); - if ($def === null) { - return []; - } - return Location::fromNode($def); + return coroutine(function () use ($textDocument, $position) { + $document = yield $this->project->getOrLoadDocument($textDocument->uri); + $node = $document->getNodeAtPosition($position); + if ($node === null) { + return []; + } + $def = yield $document->getDefinitionByNode($node); + if ($def === null) { + return []; + } + return Location::fromNode($def); + }); } /** @@ -161,66 +174,68 @@ class TextDocument * * @param TextDocumentIdentifier $textDocument The text document * @param Position $position The position inside the text document - * @return Hover + * @return Promise */ - public function hover(TextDocumentIdentifier $textDocument, Position $position): Hover + public function hover(TextDocumentIdentifier $textDocument, Position $position): Promise { - $document = $this->project->getDocument($textDocument->uri); - // Find the node under the cursor - $node = $document->getNodeAtPosition($position); - if ($node === null) { - return new Hover([]); - } - $range = Range::fromNode($node); - // Get the definition node for whatever node is under the cursor - $def = $document->getDefinitionByNode($node); - if ($def === null) { - return new Hover([], $range); - } - $contents = []; + return coroutine(function () use ($textDocument, $position) { + $document = yield $this->project->getOrLoadDocument($textDocument->uri); + // Find the node under the cursor + $node = $document->getNodeAtPosition($position); + if ($node === null) { + return new Hover([]); + } + $range = Range::fromNode($node); + // Get the definition node for whatever node is under the cursor + $def = yield $document->getDefinitionByNode($node); + if ($def === null) { + return new Hover([], $range); + } + $contents = []; - // Build a declaration string - if ($def instanceof Node\Stmt\PropertyProperty || $def instanceof Node\Const_) { - // Properties and constants can have multiple declarations - // Use the parent node (that includes the modifiers), but only render the requested declaration - $child = $def; - $def = $def->getAttribute('parentNode'); - $defLine = clone $def; - $defLine->props = [$child]; - } else { - $defLine = clone $def; - } - // Don't include the docblock in the declaration string - $defLine->setAttribute('comments', []); - if (isset($defLine->stmts)) { - $defLine->stmts = []; - } - $defText = $this->prettyPrinter->prettyPrint([$defLine]); - $lines = explode("\n", $defText); - if (isset($lines[0])) { - $contents[] = new MarkedString('php', "getAttribute('parentNode'); + $defLine = clone $def; + $defLine->props = [$child]; + } else { + $defLine = clone $def; + } + // Don't include the docblock in the declaration string + $defLine->setAttribute('comments', []); + if (isset($defLine->stmts)) { + $defLine->stmts = []; + } + $defText = $this->prettyPrinter->prettyPrint([$defLine]); + $lines = explode("\n", $defText); + if (isset($lines[0])) { + $contents[] = new MarkedString('php', "getAttribute('parentNode'); - $docBlock = $fn->getAttribute('docBlock'); - if ($docBlock !== null) { - $tags = $docBlock->getTagsByName('param'); - foreach ($tags as $tag) { - if ($tag->getVariableName() === $def->name) { - $contents[] = $tag->getDescription()->render(); - break; + // Get the documentation string + if ($def instanceof Node\Param) { + $fn = $def->getAttribute('parentNode'); + $docBlock = $fn->getAttribute('docBlock'); + if ($docBlock !== null) { + $tags = $docBlock->getTagsByName('param'); + foreach ($tags as $tag) { + if ($tag->getVariableName() === $def->name) { + $contents[] = $tag->getDescription()->render(); + break; + } } } + } else { + $docBlock = $def->getAttribute('docBlock'); + if ($docBlock !== null) { + $contents[] = $docBlock->getSummary(); + } } - } else { - $docBlock = $def->getAttribute('docBlock'); - if ($docBlock !== null) { - $contents[] = $docBlock->getSummary(); - } - } - return new Hover($contents, $range); + return new Hover($contents, $range); + }); } } diff --git a/src/utils.php b/src/utils.php index 58b07ea..061eff7 100644 --- a/src/utils.php +++ b/src/utils.php @@ -3,26 +3,9 @@ declare(strict_types = 1); namespace LanguageServer; +use Throwable; use InvalidArgumentException; - -/** - * Recursively Searches files with matching filename, starting at $path. - * - * @param string $path - * @param string $pattern - * @return array - */ -function findFilesRecursive(string $path, string $pattern): array -{ - $dir = new \RecursiveDirectoryIterator($path); - $ite = new \RecursiveIteratorIterator($dir); - $files = new \RegexIterator($ite, $pattern, \RegexIterator::GET_MATCH); - $fileList = []; - foreach ($files as $file) { - $fileList = array_merge($fileList, $file); - } - return $fileList; -} +use Sabre\Event\{Loop, Promise}; /** * Transforms an absolute file path into a URI as used by the language server protocol. @@ -66,3 +49,31 @@ function uriToPath(string $uri) } return $filepath; } + +/** + * Throws an exception on the next tick. + * Useful for letting a promise crash the process on rejection. + * + * @param Throwable $err + * @return void + */ +function crash(Throwable $err) +{ + Loop\nextTick(function () use ($err) { + throw $err; + }); +} + +/** + * Returns a promise that is resolved after x seconds. + * Useful for giving back control to the event loop inside a coroutine. + * + * @param int $seconds + * @return Promise + */ +function timeout($seconds = 0): Promise +{ + $promise = new Promise; + Loop\setTimeout([$promise, 'fulfill'], $seconds); + return $promise; +} diff --git a/tests/LanguageServerTest.php b/tests/LanguageServerTest.php index 3f0c314..8456713 100644 --- a/tests/LanguageServerTest.php +++ b/tests/LanguageServerTest.php @@ -5,9 +5,13 @@ namespace LanguageServer\Tests; use PHPUnit\Framework\TestCase; use LanguageServer\LanguageServer; -use LanguageServer\Protocol\{Message, ClientCapabilities, TextDocumentSyncKind, MessageType}; +use LanguageServer\Protocol\{ + Message, ClientCapabilities, TextDocumentSyncKind, MessageType, TextDocumentItem, TextDocumentIdentifier}; use AdvancedJsonRpc; +use Webmozart\Glob\Glob; +use Webmozart\PathUtil\Path; use Sabre\Event\Promise; +use Exception; use function LanguageServer\pathToUri; class LanguageServerTest extends TestCase @@ -17,15 +21,14 @@ class LanguageServerTest extends TestCase $reader = new MockProtocolStream(); $writer = new MockProtocolStream(); $server = new LanguageServer($reader, $writer); - $msg = null; - $writer->on('message', function (Message $message) use (&$msg) { - $msg = $message; - }); + $promise = new Promise; + $writer->once('message', [$promise, 'fulfill']); $reader->write(new Message(new AdvancedJsonRpc\Request(1, 'initialize', [ 'rootPath' => __DIR__, 'processId' => getmypid(), 'capabilities' => new ClientCapabilities() ]))); + $msg = $promise->wait(); $this->assertNotNull($msg, 'message event should be emitted'); $this->assertInstanceOf(AdvancedJsonRpc\SuccessResponse::class, $msg->body); $this->assertEquals((object)[ @@ -49,7 +52,7 @@ class LanguageServerTest extends TestCase ], $msg->body->result); } - public function testIndexing() + public function testIndexingWithDirectFileAccess() { $promise = new Promise; $input = new MockProtocolStream; @@ -68,4 +71,54 @@ class LanguageServerTest extends TestCase $server->initialize(getmypid(), $capabilities, realpath(__DIR__ . '/../fixtures')); $promise->wait(); } + + public function testIndexingWithFilesAndContentRequests() + { + $promise = new Promise; + $filesCalled = false; + $contentCalled = false; + $rootPath = realpath(__DIR__ . '/../fixtures'); + $input = new MockProtocolStream; + $output = new MockProtocolStream; + $output->on('message', function (Message $msg) use ($promise, $input, $rootPath, &$filesCalled, &$contentCalled) { + 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)); + } + } else if (strpos($msg->body->params->message, 'All 10 PHP files parsed') !== false) { + // Indexing finished + $promise->fulfill(); + } + } + }); + $server = new LanguageServer($input, $output); + $capabilities = new ClientCapabilities; + $capabilities->xfilesProvider = true; + $capabilities->xcontentProvider = true; + $server->initialize(getmypid(), $capabilities, $rootPath); + $promise->wait(); + $this->assertTrue($filesCalled); + $this->assertTrue($contentCalled); + } } diff --git a/tests/MockProtocolStream.php b/tests/MockProtocolStream.php index 5550b3f..b2a489d 100644 --- a/tests/MockProtocolStream.php +++ b/tests/MockProtocolStream.php @@ -5,7 +5,7 @@ namespace LanguageServer\Tests; use LanguageServer\{ProtocolReader, ProtocolWriter}; use LanguageServer\Protocol\Message; -use Sabre\Event\{Emitter, Promise}; +use Sabre\Event\{Loop, Emitter, Promise}; /** * A fake duplex protocol stream @@ -20,7 +20,9 @@ class MockProtocolStream extends Emitter implements ProtocolReader, ProtocolWrit */ public function write(Message $msg): Promise { - $this->emit('message', [Message::parse((string)$msg)]); + Loop\nextTick(function () use ($msg) { + $this->emit('message', [Message::parse((string)$msg)]); + }); return Promise\resolve(null); } } diff --git a/tests/NodeVisitor/DefinitionCollectorTest.php b/tests/NodeVisitor/DefinitionCollectorTest.php index 0a8fdb1..6df5940 100644 --- a/tests/NodeVisitor/DefinitionCollectorTest.php +++ b/tests/NodeVisitor/DefinitionCollectorTest.php @@ -7,6 +7,7 @@ use PHPUnit\Framework\TestCase; use PhpParser\{NodeTraverser, Node}; use PhpParser\NodeVisitor\NameResolver; use LanguageServer\{LanguageClient, Project, PhpDocument, Parser}; +use LanguageServer\Protocol\ClientCapabilities; use LanguageServer\Tests\MockProtocolStream; use LanguageServer\NodeVisitor\{ReferencesAdder, DefinitionCollector}; use function LanguageServer\pathToUri; @@ -16,10 +17,10 @@ class DefinitionCollectorTest extends TestCase public function testCollectsSymbols() { $client = new LanguageClient(new MockProtocolStream, new MockProtocolStream); - $project = new Project($client); + $project = new Project($client, new ClientCapabilities); $parser = new Parser; $uri = pathToUri(realpath(__DIR__ . '/../../fixtures/symbols.php')); - $document = $project->loadDocument($uri); + $document = $project->loadDocument($uri)->wait(); $traverser = new NodeTraverser; $traverser->addVisitor(new NameResolver); $traverser->addVisitor(new ReferencesAdder($document)); @@ -55,10 +56,10 @@ class DefinitionCollectorTest extends TestCase public function testDoesNotCollectReferences() { $client = new LanguageClient(new MockProtocolStream, new MockProtocolStream); - $project = new Project($client); + $project = new Project($client, new ClientCapabilities); $parser = new Parser; $uri = pathToUri(realpath(__DIR__ . '/../../fixtures/references.php')); - $document = $project->loadDocument($uri); + $document = $project->loadDocument($uri)->wait(); $traverser = new NodeTraverser; $traverser->addVisitor(new NameResolver); $traverser->addVisitor(new ReferencesAdder($document)); diff --git a/tests/PhpDocumentTest.php b/tests/PhpDocumentTest.php index fce5dc4..5cdc8c8 100644 --- a/tests/PhpDocumentTest.php +++ b/tests/PhpDocumentTest.php @@ -7,7 +7,7 @@ use PHPUnit\Framework\TestCase; use LanguageServer\Tests\MockProtocolStream; use LanguageServer\{LanguageClient, Project}; use LanguageServer\NodeVisitor\NodeAtPositionFinder; -use LanguageServer\Protocol\{SymbolKind, Position}; +use LanguageServer\Protocol\{SymbolKind, Position, ClientCapabilities}; use PhpParser\Node; class PhpDocumentTest extends TestCase @@ -19,7 +19,8 @@ class PhpDocumentTest extends TestCase public function setUp() { - $this->project = new Project(new LanguageClient(new MockProtocolStream, new MockProtocolStream)); + $client = new LanguageClient(new MockProtocolStream, new MockProtocolStream); + $this->project = new Project($client, new ClientCapabilities); } public function testParsesVariableVariables() diff --git a/tests/ProjectTest.php b/tests/ProjectTest.php index dc030d2..161370c 100644 --- a/tests/ProjectTest.php +++ b/tests/ProjectTest.php @@ -6,7 +6,14 @@ namespace LanguageServer\Tests\Server; use PHPUnit\Framework\TestCase; use LanguageServer\Tests\MockProtocolStream; use LanguageServer\{Server, Client, LanguageClient, Project, PhpDocument}; -use LanguageServer\Protocol\{TextDocumentItem, TextDocumentIdentifier, SymbolKind, DiagnosticSeverity, FormattingOptions}; +use LanguageServer\Protocol\{ + TextDocumentItem, + TextDocumentIdentifier, + SymbolKind, + DiagnosticSeverity, + FormattingOptions, + ClientCapabilities +}; use AdvancedJsonRpc\{Request as RequestBody, Response as ResponseBody}; use function LanguageServer\pathToUri; @@ -19,12 +26,13 @@ class ProjectTest extends TestCase public function setUp() { - $this->project = new Project(new LanguageClient(new MockProtocolStream, new MockProtocolStream)); + $client = new LanguageClient(new MockProtocolStream, new MockProtocolStream); + $this->project = new Project($client, new ClientCapabilities); } - public function testGetDocumentLoadsDocument() + public function testGetOrLoadDocumentLoadsDocument() { - $document = $this->project->getDocument(pathToUri(__FILE__)); + $document = $this->project->getOrLoadDocument(pathToUri(__FILE__))->wait(); $this->assertNotNull($document); $this->assertInstanceOf(PhpDocument::class, $document); diff --git a/tests/Server/ServerTestCase.php b/tests/Server/ServerTestCase.php index 24095a3..c5818a9 100644 --- a/tests/Server/ServerTestCase.php +++ b/tests/Server/ServerTestCase.php @@ -6,8 +6,9 @@ namespace LanguageServer\Tests\Server; use PHPUnit\Framework\TestCase; use LanguageServer\Tests\MockProtocolStream; use LanguageServer\{Server, LanguageClient, Project}; -use LanguageServer\Protocol\{Position, Location, Range}; +use LanguageServer\Protocol\{Position, Location, Range, ClientCapabilities}; use function LanguageServer\pathToUri; +use Sabre\Event\Promise; abstract class ServerTestCase extends TestCase { @@ -43,7 +44,7 @@ abstract class ServerTestCase extends TestCase public function setUp() { $client = new LanguageClient(new MockProtocolStream, new MockProtocolStream); - $this->project = new Project($client); + $this->project = new Project($client, new ClientCapabilities); $this->textDocument = new Server\TextDocument($this->project, $client); $this->workspace = new Server\Workspace($this->project, $client); @@ -53,11 +54,13 @@ abstract class ServerTestCase extends TestCase $referencesUri = pathToUri(realpath(__DIR__ . '/../../fixtures/references.php')); $useUri = pathToUri(realpath(__DIR__ . '/../../fixtures/use.php')); - $this->project->loadDocument($symbolsUri); - $this->project->loadDocument($referencesUri); - $this->project->loadDocument($globalSymbolsUri); - $this->project->loadDocument($globalReferencesUri); - $this->project->loadDocument($useUri); + Promise\all([ + $this->project->loadDocument($symbolsUri), + $this->project->loadDocument($referencesUri), + $this->project->loadDocument($globalSymbolsUri), + $this->project->loadDocument($globalReferencesUri), + $this->project->loadDocument($useUri) + ])->wait(); // @codingStandardsIgnoreStart $this->definitionLocations = [ diff --git a/tests/Server/TextDocument/Definition/GlobalFallbackTest.php b/tests/Server/TextDocument/Definition/GlobalFallbackTest.php index ea62f70..c4b021d 100644 --- a/tests/Server/TextDocument/Definition/GlobalFallbackTest.php +++ b/tests/Server/TextDocument/Definition/GlobalFallbackTest.php @@ -6,14 +6,15 @@ namespace LanguageServer\Tests\Server\TextDocument\Definition; use LanguageServer\Tests\MockProtocolStream; use LanguageServer\Tests\Server\ServerTestCase; use LanguageServer\{Server, LanguageClient, Project}; -use LanguageServer\Protocol\{TextDocumentIdentifier, Position, Range, Location}; +use LanguageServer\Protocol\{TextDocumentIdentifier, Position, Range, Location, ClientCapabilities}; +use Sabre\Event\Promise; class GlobalFallbackTest extends ServerTestCase { public function setUp() { $client = new LanguageClient(new MockProtocolStream, new MockProtocolStream); - $project = new Project($client); + $project = new Project($client, new ClientCapabilities); $this->textDocument = new Server\TextDocument($project, $client); $project->openDocument('global_fallback', file_get_contents(__DIR__ . '/../../../../fixtures/global_fallback.php')); $project->openDocument('global_symbols', file_get_contents(__DIR__ . '/../../../../fixtures/global_symbols.php')); @@ -23,7 +24,10 @@ class GlobalFallbackTest extends ServerTestCase { // $obj = new TestClass(); // Get definition for TestClass should not fall back to global - $result = $this->textDocument->definition(new TextDocumentIdentifier('global_fallback'), new Position(9, 16)); + $result = $this->textDocument->definition( + new TextDocumentIdentifier('global_fallback'), + new Position(9, 16) + )->wait(); $this->assertEquals([], $result); } @@ -31,7 +35,10 @@ class GlobalFallbackTest extends ServerTestCase { // echo TEST_CONST; // Get definition for TEST_CONST - $result = $this->textDocument->definition(new TextDocumentIdentifier('global_fallback'), new Position(6, 10)); + $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); } @@ -39,7 +46,10 @@ class GlobalFallbackTest extends ServerTestCase { // test_function(); // Get definition for test_function - $result = $this->textDocument->definition(new TextDocumentIdentifier('global_fallback'), new Position(5, 6)); + $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); } } diff --git a/tests/Server/TextDocument/Definition/GlobalTest.php b/tests/Server/TextDocument/Definition/GlobalTest.php index ded6f38..bc5f6e6 100644 --- a/tests/Server/TextDocument/Definition/GlobalTest.php +++ b/tests/Server/TextDocument/Definition/GlobalTest.php @@ -12,14 +12,20 @@ class GlobalTest extends ServerTestCase public function testDefinitionFileBeginning() { // |textDocument->definition(new TextDocumentIdentifier(pathToUri(realpath(__DIR__ . '/../../../../fixtures/references.php'))), new Position(0, 0)); + $result = $this->textDocument->definition( + new TextDocumentIdentifier(pathToUri(realpath(__DIR__ . '/../../../../fixtures/references.php'))), + new Position(0, 0) + )->wait(); $this->assertEquals([], $result); } public function testDefinitionEmptyResult() { // namespace keyword - $result = $this->textDocument->definition(new TextDocumentIdentifier(pathToUri(realpath(__DIR__ . '/../../../../fixtures/references.php'))), new Position(2, 4)); + $result = $this->textDocument->definition( + new TextDocumentIdentifier(pathToUri(realpath(__DIR__ . '/../../../../fixtures/references.php'))), + new Position(2, 4) + )->wait(); $this->assertEquals([], $result); } @@ -28,7 +34,10 @@ class GlobalTest extends ServerTestCase // $obj = new TestClass(); // Get definition for TestClass $reference = $this->getReferenceLocations('TestClass')[0]; - $result = $this->textDocument->definition(new TextDocumentIdentifier($reference->uri), $reference->range->start); + $result = $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + )->wait(); $this->assertEquals($this->getDefinitionLocation('TestClass'), $result); } @@ -37,7 +46,10 @@ class GlobalTest extends ServerTestCase // TestClass::staticTestMethod(); // Get definition for TestClass $reference = $this->getReferenceLocations('TestClass')[1]; - $result = $this->textDocument->definition(new TextDocumentIdentifier($reference->uri), $reference->range->start); + $result = $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + )->wait(); $this->assertEquals($this->getDefinitionLocation('TestClass'), $result); } @@ -46,7 +58,10 @@ class GlobalTest extends ServerTestCase // echo TestClass::$staticTestProperty; // Get definition for TestClass $reference = $this->getReferenceLocations('TestClass')[2]; - $result = $this->textDocument->definition(new TextDocumentIdentifier($reference->uri), $reference->range->start); + $result = $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + )->wait(); $this->assertEquals($this->getDefinitionLocation('TestClass'), $result); } @@ -55,7 +70,10 @@ class GlobalTest extends ServerTestCase // TestClass::TEST_CLASS_CONST; // Get definition for TestClass $reference = $this->getReferenceLocations('TestClass')[3]; - $result = $this->textDocument->definition(new TextDocumentIdentifier($reference->uri), $reference->range->start); + $result = $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + )->wait(); $this->assertEquals($this->getDefinitionLocation('TestClass'), $result); } @@ -64,7 +82,10 @@ class GlobalTest extends ServerTestCase // class TestClass implements TestInterface // Get definition for TestInterface $reference = $this->getReferenceLocations('TestInterface')[0]; - $result = $this->textDocument->definition(new TextDocumentIdentifier($reference->uri), $reference->range->start); + $result = $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + )->wait(); $this->assertEquals($this->getDefinitionLocation('TestInterface'), $result); } @@ -73,7 +94,10 @@ class GlobalTest extends ServerTestCase // 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); + $result = $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + )->wait(); $this->assertEquals($this->getDefinitionLocation('TestClass::TEST_CLASS_CONST'), $result); } @@ -82,7 +106,10 @@ class GlobalTest extends ServerTestCase // 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); + $result = $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + )->wait(); $this->assertEquals($this->getDefinitionLocation('TestClass::TEST_CLASS_CONST'), $result); } @@ -91,7 +118,10 @@ class GlobalTest extends ServerTestCase // 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); + $result = $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + )->wait(); $this->assertEquals($this->getDefinitionLocation('TEST_CONST'), $result); } @@ -100,7 +130,10 @@ class GlobalTest extends ServerTestCase // TestClass::staticTestMethod(); // Get definition for staticTestMethod $reference = $this->getReferenceLocations('TestClass::staticTestMethod()')[0]; - $result = $this->textDocument->definition(new TextDocumentIdentifier($reference->uri), $reference->range->end); + $result = $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + )->wait(); $this->assertEquals($this->getDefinitionLocation('TestClass::staticTestMethod()'), $result); } @@ -109,7 +142,10 @@ class GlobalTest extends ServerTestCase // echo TestClass::$staticTestProperty; // Get definition for staticTestProperty $reference = $this->getReferenceLocations('TestClass::staticTestProperty')[0]; - $result = $this->textDocument->definition(new TextDocumentIdentifier($reference->uri), $reference->range->end); + $result = $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + )->wait(); $this->assertEquals($this->getDefinitionLocation('TestClass::staticTestProperty'), $result); } @@ -118,7 +154,10 @@ class GlobalTest extends ServerTestCase // $obj->testMethod(); // Get definition for testMethod $reference = $this->getReferenceLocations('TestClass::testMethod()')[0]; - $result = $this->textDocument->definition(new TextDocumentIdentifier($reference->uri), $reference->range->end); + $result = $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + )->wait(); $this->assertEquals($this->getDefinitionLocation('TestClass::testMethod()'), $result); } @@ -127,7 +166,10 @@ class GlobalTest extends ServerTestCase // echo $obj->testProperty; // Get definition for testProperty $reference = $this->getReferenceLocations('TestClass::testProperty')[1]; - $result = $this->textDocument->definition(new TextDocumentIdentifier($reference->uri), $reference->range->end); + $result = $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + )->wait(); $this->assertEquals($this->getDefinitionLocation('TestClass::testProperty'), $result); } @@ -136,7 +178,10 @@ class GlobalTest extends ServerTestCase // $this->testProperty = $testParameter; // Get definition for testProperty $reference = $this->getReferenceLocations('TestClass::testProperty')[0]; - $result = $this->textDocument->definition(new TextDocumentIdentifier($reference->uri), $reference->range->end); + $result = $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + )->wait(); $this->assertEquals($this->getDefinitionLocation('TestClass::testProperty'), $result); } @@ -145,7 +190,10 @@ class GlobalTest extends ServerTestCase // echo $var; // Get definition for $var $uri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/references.php')); - $result = $this->textDocument->definition(new TextDocumentIdentifier($uri), new Position(13, 7)); + $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); } @@ -154,7 +202,10 @@ class GlobalTest extends ServerTestCase // function whatever(TestClass $param) { // Get definition for TestClass $reference = $this->getReferenceLocations('TestClass')[4]; - $result = $this->textDocument->definition(new TextDocumentIdentifier($reference->uri), $reference->range->start); + $result = $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + )->wait(); $this->assertEquals($this->getDefinitionLocation('TestClass'), $result); } @@ -163,7 +214,10 @@ class GlobalTest extends ServerTestCase // function whatever(TestClass $param): TestClass { // Get definition for TestClass $reference = $this->getReferenceLocations('TestClass')[5]; - $result = $this->textDocument->definition(new TextDocumentIdentifier($reference->uri), $reference->range->start); + $result = $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + )->wait(); $this->assertEquals($this->getDefinitionLocation('TestClass'), $result); } @@ -172,7 +226,10 @@ class GlobalTest extends ServerTestCase // 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); + $result = $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + )->wait(); $this->assertEquals($this->getDefinitionLocation('TestInterface'), $result); } @@ -181,7 +238,10 @@ class GlobalTest extends ServerTestCase // echo $param; // Get definition for $param $uri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/references.php')); - $result = $this->textDocument->definition(new TextDocumentIdentifier($uri), new Position(22, 13)); + $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); } @@ -190,7 +250,10 @@ class GlobalTest extends ServerTestCase // echo $var; // Get definition for $var $uri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/references.php')); - $result = $this->textDocument->definition(new TextDocumentIdentifier($uri), new Position(26, 11)); + $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); } @@ -199,7 +262,10 @@ class GlobalTest extends ServerTestCase // test_function(); // Get definition for test_function $reference = $this->getReferenceLocations('test_function()')[0]; - $result = $this->textDocument->definition(new TextDocumentIdentifier($reference->uri), $reference->range->start); + $result = $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + )->wait(); $this->assertEquals($this->getDefinitionLocation('test_function()'), $result); } @@ -208,7 +274,10 @@ class GlobalTest extends ServerTestCase // 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); + $result = $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + )->wait(); $this->assertEquals($this->getDefinitionLocation('test_function()'), $result); } @@ -217,7 +286,10 @@ class GlobalTest extends ServerTestCase // if ($abc instanceof TestInterface) { // Get definition for TestInterface $reference = $this->getReferenceLocations('TestInterface')[2]; - $result = $this->textDocument->definition(new TextDocumentIdentifier($reference->uri), $reference->range->start); + $result = $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + )->wait(); $this->assertEquals($this->getDefinitionLocation('TestInterface'), $result); } } diff --git a/tests/Server/TextDocument/Definition/NamespacedTest.php b/tests/Server/TextDocument/Definition/NamespacedTest.php index e796ef9..ef66624 100644 --- a/tests/Server/TextDocument/Definition/NamespacedTest.php +++ b/tests/Server/TextDocument/Definition/NamespacedTest.php @@ -23,7 +23,10 @@ class NamespacedTest extends GlobalTest // echo TEST_CONST; // Get definition for TEST_CONST $reference = $this->getReferenceLocations('TEST_CONST')[0]; - $result = $this->textDocument->definition(new TextDocumentIdentifier($reference->uri), $reference->range->start); + $result = $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + )->wait(); $this->assertEquals($this->getDefinitionLocation('TEST_CONST'), $result); } @@ -32,7 +35,10 @@ class NamespacedTest extends GlobalTest // use TestNamespace\TestClass; // Get definition for TestClass $reference = $this->getReferenceLocations('TestClass')[6]; - $result = $this->textDocument->definition(new TextDocumentIdentifier($reference->uri), $reference->range->start); + $result = $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + )->wait(); $this->assertEquals($this->getDefinitionLocation('TestClass'), $result); } @@ -41,7 +47,10 @@ class NamespacedTest extends GlobalTest // use TestNamespace\{TestTrait, TestInterface}; // Get definition for TestInterface $reference = $this->getReferenceLocations('TestClass')[0]; - $result = $this->textDocument->definition(new TextDocumentIdentifier($reference->uri), $reference->range->start); + $result = $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + )->wait(); $this->assertEquals($this->getDefinitionLocation('TestClass'), $result); } } diff --git a/tests/Server/TextDocument/DidChangeTest.php b/tests/Server/TextDocument/DidChangeTest.php index 0ee62ef..1df0505 100644 --- a/tests/Server/TextDocument/DidChangeTest.php +++ b/tests/Server/TextDocument/DidChangeTest.php @@ -12,7 +12,8 @@ use LanguageServer\Protocol\{ VersionedTextDocumentIdentifier, TextDocumentContentChangeEvent, Range, - Position + Position, + ClientCapabilities }; class DidChangeTest extends TestCase @@ -20,7 +21,7 @@ class DidChangeTest extends TestCase public function test() { $client = new LanguageClient(new MockProtocolStream, new MockProtocolStream); - $project = new Project($client); + $project = new Project($client, new ClientCapabilities); $textDocument = new Server\TextDocument($project, $client); $phpDocument = $project->openDocument('whatever', "openDocument('whatever', 'hello world'); diff --git a/tests/Server/TextDocument/DocumentSymbolTest.php b/tests/Server/TextDocument/DocumentSymbolTest.php index 7b304d0..4f09e20 100644 --- a/tests/Server/TextDocument/DocumentSymbolTest.php +++ b/tests/Server/TextDocument/DocumentSymbolTest.php @@ -15,7 +15,7 @@ class DocumentSymbolTest extends ServerTestCase { // Request symbols $uri = pathToUri(realpath(__DIR__ . '/../../../fixtures/symbols.php')); - $result = $this->textDocument->documentSymbol(new TextDocumentIdentifier($uri)); + $result = $this->textDocument->documentSymbol(new TextDocumentIdentifier($uri))->wait(); // @codingStandardsIgnoreStart $this->assertEquals([ new SymbolInformation('TEST_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestNamespace\\TEST_CONST'), 'TestNamespace'), diff --git a/tests/Server/TextDocument/FormattingTest.php b/tests/Server/TextDocument/FormattingTest.php index ca6fc2c..b7d0609 100644 --- a/tests/Server/TextDocument/FormattingTest.php +++ b/tests/Server/TextDocument/FormattingTest.php @@ -6,7 +6,15 @@ namespace LanguageServer\Tests\Server\TextDocument; use PHPUnit\Framework\TestCase; use LanguageServer\Tests\MockProtocolStream; use LanguageServer\{Server, Client, LanguageClient, Project}; -use LanguageServer\Protocol\{TextDocumentIdentifier, TextDocumentItem, FormattingOptions}; +use LanguageServer\Protocol\{ + TextDocumentIdentifier, + TextDocumentItem, + FormattingOptions, + ClientCapabilities, + TextEdit, + Range, + Position +}; use function LanguageServer\{pathToUri, uriToPath}; class FormattingTest extends TestCase @@ -19,14 +27,14 @@ class FormattingTest extends TestCase public function setUp() { $client = new LanguageClient(new MockProtocolStream, new MockProtocolStream); - $project = new Project($client); + $project = new Project($client, new ClientCapabilities); $this->textDocument = new Server\TextDocument($project, $client); } public function testFormatting() { $client = new LanguageClient(new MockProtocolStream, new MockProtocolStream); - $project = new Project($client); + $project = new Project($client, new ClientCapabilities); $textDocument = new Server\TextDocument($project, $client); $path = realpath(__DIR__ . '/../../../fixtures/format.php'); $uri = pathToUri($path); @@ -42,19 +50,7 @@ class FormattingTest extends TestCase // how code should look after formatting $expected = file_get_contents(__DIR__ . '/../../../fixtures/format_expected.php'); // Request formatting - $result = $textDocument->formatting(new TextDocumentIdentifier($uri), new FormattingOptions()); - $this->assertEquals([0 => [ - 'range' => [ - 'start' => [ - 'line' => 0, - 'character' => 0 - ], - 'end' => [ - 'line' => 20, - 'character' => 0 - ] - ], - 'newText' => $expected - ]], json_decode(json_encode($result), true)); + $result = $textDocument->formatting(new TextDocumentIdentifier($uri), new FormattingOptions())->wait(); + $this->assertEquals([new TextEdit(new Range(new Position(0, 0), new Position(20, 0)), $expected)], $result); } } diff --git a/tests/Server/TextDocument/HoverTest.php b/tests/Server/TextDocument/HoverTest.php index 20d59d0..80cef64 100644 --- a/tests/Server/TextDocument/HoverTest.php +++ b/tests/Server/TextDocument/HoverTest.php @@ -16,7 +16,10 @@ class HoverTest extends ServerTestCase // $obj = new TestClass(); // Get hover for TestClass $reference = $this->getReferenceLocations('TestClass')[0]; - $result = $this->textDocument->hover(new TextDocumentIdentifier($reference->uri), $reference->range->start); + $result = $this->textDocument->hover( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + )->wait(); $this->assertEquals(new Hover([ new MarkedString('php', "testMethod(); // Get hover for testMethod $reference = $this->getReferenceLocations('TestClass::testMethod()')[0]; - $result = $this->textDocument->hover(new TextDocumentIdentifier($reference->uri), $reference->range->end); + $result = $this->textDocument->hover( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + )->wait(); $this->assertEquals(new Hover([ new MarkedString('php', "testProperty; // Get hover for testProperty $reference = $this->getReferenceLocations('TestClass::testProperty')[0]; - $result = $this->textDocument->hover(new TextDocumentIdentifier($reference->uri), $reference->range->end); + $result = $this->textDocument->hover( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + )->wait(); $this->assertEquals(new Hover([ new MarkedString('php', "getReferenceLocations('TestClass::staticTestMethod()')[0]; - $result = $this->textDocument->hover(new TextDocumentIdentifier($reference->uri), $reference->range->end); + $result = $this->textDocument->hover( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + )->wait(); $this->assertEquals(new Hover([ new MarkedString('php', "getReferenceLocations('TestClass::staticTestProperty')[0]; - $result = $this->textDocument->hover(new TextDocumentIdentifier($reference->uri), $reference->range->end); + $result = $this->textDocument->hover( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + )->wait(); $this->assertEquals(new Hover([ new MarkedString('php', "getReferenceLocations('TestClass::TEST_CLASS_CONST')[0]; - $result = $this->textDocument->hover(new TextDocumentIdentifier($reference->uri), $reference->range->end); + $result = $this->textDocument->hover( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + )->wait(); $this->assertEquals(new Hover([ new MarkedString('php', "getReferenceLocations('test_function()')[0]; - $result = $this->textDocument->hover(new TextDocumentIdentifier($reference->uri), $reference->range->end); + $result = $this->textDocument->hover( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + )->wait(); $this->assertEquals(new Hover([ new MarkedString('php', "getReferenceLocations('TEST_CONST')[0]; - $result = $this->textDocument->hover(new TextDocumentIdentifier($reference->uri), $reference->range->end); + $result = $this->textDocument->hover( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + )->wait(); $this->assertEquals(new Hover([ new MarkedString('php', "textDocument->hover(new TextDocumentIdentifier($uri), new Position(13, 7)); + $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(22, 11)); + $result = $this->textDocument->hover(new TextDocumentIdentifier($uri), new Position(22, 11))->wait(); $this->assertEquals(new Hover( [ new MarkedString('php', "args = &$args; } public function publishDiagnostics(string $uri, array $diagnostics): Promise @@ -34,7 +35,7 @@ class ParseErrorsTest extends TestCase return Promise\resolve(null); } }; - $project = new Project($client); + $project = new Project($client, new ClientCapabilities); $this->textDocument = new Server\TextDocument($project, $client); } diff --git a/tests/Server/TextDocument/References/GlobalFallbackTest.php b/tests/Server/TextDocument/References/GlobalFallbackTest.php index 6862d97..9f68cb9 100644 --- a/tests/Server/TextDocument/References/GlobalFallbackTest.php +++ b/tests/Server/TextDocument/References/GlobalFallbackTest.php @@ -6,7 +6,7 @@ namespace LanguageServer\Tests\Server\TextDocument\References; use PHPUnit\Framework\TestCase; use LanguageServer\Tests\MockProtocolStream; use LanguageServer\{Server, LanguageClient, Project}; -use LanguageServer\Protocol\{TextDocumentIdentifier, Position, ReferenceContext, Location, Range}; +use LanguageServer\Protocol\{TextDocumentIdentifier, Position, ReferenceContext, Location, Range, ClientCapabilities}; use LanguageServer\Tests\Server\ServerTestCase; class GlobalFallbackTest extends ServerTestCase @@ -14,7 +14,7 @@ class GlobalFallbackTest extends ServerTestCase public function setUp() { $client = new LanguageClient(new MockProtocolStream, new MockProtocolStream); - $project = new Project($client); + $project = new Project($client, new ClientCapabilities); $this->textDocument = new Server\TextDocument($project, $client); $project->openDocument('global_fallback', file_get_contents(__DIR__ . '/../../../../fixtures/global_fallback.php')); $project->openDocument('global_symbols', file_get_contents(__DIR__ . '/../../../../fixtures/global_symbols.php')); @@ -24,7 +24,11 @@ class GlobalFallbackTest extends ServerTestCase { // class TestClass implements TestInterface // Get references for TestClass - $result = $this->textDocument->references(new ReferenceContext, new TextDocumentIdentifier('global_symbols'), new Position(6, 9)); + $result = $this->textDocument->references( + new ReferenceContext, + new TextDocumentIdentifier('global_symbols'), + new Position(6, 9) + )->wait(); $this->assertEquals([], $result); } @@ -32,7 +36,11 @@ class GlobalFallbackTest extends ServerTestCase { // const TEST_CONST = 123; // Get references for TEST_CONST - $result = $this->textDocument->references(new ReferenceContext, new TextDocumentIdentifier('global_symbols'), new Position(9, 13)); + $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); } @@ -40,7 +48,11 @@ class GlobalFallbackTest extends ServerTestCase { // function test_function() // Get references for test_function - $result = $this->textDocument->references(new ReferenceContext, new TextDocumentIdentifier('global_symbols'), new Position(78, 16)); + $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); } } diff --git a/tests/Server/TextDocument/References/GlobalTest.php b/tests/Server/TextDocument/References/GlobalTest.php index 1ecf1f9..bf6eb97 100644 --- a/tests/Server/TextDocument/References/GlobalTest.php +++ b/tests/Server/TextDocument/References/GlobalTest.php @@ -14,7 +14,11 @@ class GlobalTest extends ServerTestCase // class TestClass implements TestInterface // Get references for TestClass $definition = $this->getDefinitionLocation('TestClass'); - $result = $this->textDocument->references(new ReferenceContext, new TextDocumentIdentifier($definition->uri), $definition->range->start); + $result = $this->textDocument->references( + new ReferenceContext, + new TextDocumentIdentifier($definition->uri), + $definition->range->start + )->wait(); $this->assertEquals($this->getReferenceLocations('TestClass'), $result); } @@ -23,7 +27,11 @@ class GlobalTest extends ServerTestCase // 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); + $result = $this->textDocument->references( + new ReferenceContext, + new TextDocumentIdentifier($definition->uri), + $definition->range->start + )->wait(); $this->assertEquals($this->getReferenceLocations('TestClass::TEST_CLASS_CONST'), $result); } @@ -32,7 +40,11 @@ class GlobalTest extends ServerTestCase // 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); + $result = $this->textDocument->references( + new ReferenceContext, + new TextDocumentIdentifier($definition->uri), + $definition->range->start + )->wait(); $this->assertEquals($this->getReferenceLocations('TEST_CONST'), $result); } @@ -41,7 +53,11 @@ class GlobalTest extends ServerTestCase // 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); + $result = $this->textDocument->references( + new ReferenceContext, + new TextDocumentIdentifier($definition->uri), + $definition->range->start + )->wait(); $this->assertEquals($this->getReferenceLocations('TestClass::staticTestMethod()'), $result); } @@ -50,7 +66,11 @@ class GlobalTest extends ServerTestCase // 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); + $result = $this->textDocument->references( + new ReferenceContext, + new TextDocumentIdentifier($definition->uri), + $definition->range->start + )->wait(); $this->assertEquals($this->getReferenceLocations('TestClass::staticTestProperty'), $result); } @@ -59,7 +79,11 @@ class GlobalTest extends ServerTestCase // 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); + $result = $this->textDocument->references( + new ReferenceContext, + new TextDocumentIdentifier($definition->uri), + $definition->range->start + )->wait(); $this->assertEquals($this->getReferenceLocations('TestClass::testMethod()'), $result); } @@ -68,7 +92,11 @@ class GlobalTest extends ServerTestCase // public $testProperty; // Get references for testProperty $definition = $this->getDefinitionLocation('TestClass::testProperty'); - $result = $this->textDocument->references(new ReferenceContext, new TextDocumentIdentifier($definition->uri), $definition->range->start); + $result = $this->textDocument->references( + new ReferenceContext, + new TextDocumentIdentifier($definition->uri), + $definition->range->start + )->wait(); $this->assertEquals($this->getReferenceLocations('TestClass::testProperty'), $result); } @@ -77,7 +105,11 @@ class GlobalTest extends ServerTestCase // $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)); + $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))), @@ -90,7 +122,11 @@ class GlobalTest extends ServerTestCase // 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)); + $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); } @@ -100,7 +136,11 @@ class GlobalTest extends ServerTestCase // 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)); + $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))) diff --git a/tests/Utils/RecursiveFileSearchTest.php b/tests/Utils/RecursiveFileSearchTest.php deleted file mode 100644 index 671ebb1..0000000 --- a/tests/Utils/RecursiveFileSearchTest.php +++ /dev/null @@ -1,21 +0,0 @@ -assertEquals([ - $path . DIRECTORY_SEPARATOR . 'a.txt', - $path . DIRECTORY_SEPARATOR . 'search' . DIRECTORY_SEPARATOR . 'b.txt', - $path . DIRECTORY_SEPARATOR . 'search' . DIRECTORY_SEPARATOR . 'here' . DIRECTORY_SEPARATOR . 'c.txt', - ], $files); - } -}