1
0
Fork 0

style: format with prettier

prettier
Felix Becker 2018-11-27 17:51:40 +01:00
parent 8e1ea8696b
commit 4884bf8955
87 changed files with 2638 additions and 2006 deletions

104
README.md
View File

@ -17,30 +17,35 @@ Uses the great [Tolerant PHP Parser](https://github.com/Microsoft/tolerant-php-p
and an [event loop](http://sabre.io/event/loop/) for concurrency.
**Table of Contents**
- [Features](#features)
- [Performance](#performance)
- [Versioning](#versioning)
- [Installation](#installation)
- [Running](#running)
- [Used by](#used-by)
- [Contributing](#contributing)
- [Features](#features)
- [Performance](#performance)
- [Versioning](#versioning)
- [Installation](#installation)
- [Running](#running)
- [Used by](#used-by)
- [Contributing](#contributing)
## Features
### [Completion](https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#textDocument_completion)
![Completion search demo](images/completion.gif)
### [Signature Help](https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#textDocument_signatureHelp)
![Signature help demo](images/signatureHelp.gif)
### [Go To Definition](https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#goto-definition-request)
![Go To Definition demo](images/definition.gif)
### [Find References](https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#find-references-request)
![Find References demo](images/references.png)
### [Hover](https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#hover-request)
![Hover class demo](images/hoverClass.png)
![Hover parameter demo](images/hoverParam.png)
@ -49,15 +54,18 @@ A hover request returns a declaration line (marked with language `php`) and the
For Parameters, it will return the `@param` tag.
### [Document Symbols](https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#document-symbols-request)
![Document Symbols demo](images/documentSymbol.gif)
### [Workspace Symbols](https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#workspace-symbols-request)
![Workspace Symbols demo](images/workspaceSymbol.gif)
The query is matched case-insensitively against the fully qualified name of the symbol.
Non-Standard: An empty query will return _all_ symbols found in the workspace.
### Error reporting through [Publish Diagnostics](https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#publishdiagnostics-notification)
![Error reporting demo](images/publishDiagnostics.png)
PHP parse errors are reported as errors, parse errors of docblocks are reported as warnings.
@ -70,21 +78,24 @@ Completion, type resolval etc. will use the standard PHP library and common exte
### What is considered a definition?
Globally searchable definitions are:
- classes
- interfaces
- traits
- properties
- methods
- class constants
- constants with `const` keyword
- classes
- interfaces
- traits
- properties
- methods
- class constants
- constants with `const` keyword
Definitions resolved just-in-time when needed:
- variable assignments
- parameters
- closure `use` statements
- variable assignments
- parameters
- closure `use` statements
Not supported yet:
- constants with `define()`
- constants with `define()`
Namespaces are not considerd a declaration by design because they only make up a part of the fully qualified name
and don't map to one unique declaration.
@ -92,24 +103,25 @@ and don't map to one unique declaration.
### What is considered a reference?
Definitions/references/hover currently work for
- class instantiations
- static method calls
- class constant access
- static property access
- parameter type hints
- return type hints
- method calls, if the variable was assigned to a new object in the same scope
- property access, if the variable was assigned to a new object in the same scope
- variables
- parameters
- imported closure variables (`use`)
- `use` statements for classes, constants and functions
- class-like after `implements`/`extends`
- function calls
- constant access
- `instanceof` checks
- Reassigned variables
- Nested access/calls on return values, properties, array access
- class instantiations
- static method calls
- class constant access
- static property access
- parameter type hints
- return type hints
- method calls, if the variable was assigned to a new object in the same scope
- property access, if the variable was assigned to a new object in the same scope
- variables
- parameters
- imported closure variables (`use`)
- `use` statements for classes, constants and functions
- class-like after `implements`/`extends`
- function calls
- constant access
- `instanceof` checks
- Reassigned variables
- Nested access/calls on return values, properties, array access
### Protocol Extensions
@ -163,6 +175,7 @@ Start the language server with
### Command line arguments
#### `--tcp=host:port` (optional)
Causes the server to use a tcp connection for communicating with the language client instead of using STDIN/STDOUT.
The server will try to connect to the specified address.
Strongly recommended on Windows because of blocking STDIO.
@ -172,6 +185,7 @@ Example:
php bin/php-language-server.php --tcp=127.0.0.1:12345
#### `--tcp-server=host:port` (optional)
Causes the server to use a tcp connection for communicating with the language client instead of using STDIN/STDOUT.
The server will listen on the given address for a connection.
If PCNTL is available, will fork a child process for every connection.
@ -182,6 +196,7 @@ Example:
php bin/php-language-server.php --tcp-server=127.0.0.1:12345
#### `--memory-limit=integer` (optional)
Sets memory limit for language server.
Equivalent to [memory-limit](http://php.net/manual/en/ini.core.php#ini.memory-limit) php.ini directive.
The default is 4GB (which is way more than needed).
@ -191,11 +206,12 @@ Example:
php bin/php-language-server.php --memory-limit=256M
## Used by
- [VS Code PHP IntelliSense](https://github.com/felixfbecker/vscode-php-intellisense)
- [Eclipse Che](https://eclipse.org/che/)
- [Eclipse IDE (LSP4E-PHP)](https://github.com/eclipselabs/lsp4e-php)
- NeoVim: [LanguageServer-php-neovim](https://github.com/roxma/LanguageServer-php-neovim) with [LanguageClient neovim](https://github.com/autozimu/LanguageClient-neovim)
- Atom: [ide-php](https://github.com/atom/ide-php)
- [VS Code PHP IntelliSense](https://github.com/felixfbecker/vscode-php-intellisense)
- [Eclipse Che](https://eclipse.org/che/)
- [Eclipse IDE (LSP4E-PHP)](https://github.com/eclipselabs/lsp4e-php)
- NeoVim: [LanguageServer-php-neovim](https://github.com/roxma/LanguageServer-php-neovim) with [LanguageClient neovim](https://github.com/autozimu/LanguageClient-neovim)
- Atom: [ide-php](https://github.com/atom/ide-php)
## Contributing
@ -206,18 +222,18 @@ Clone the repository and run
to install dependencies.
Run the tests with
Run the tests with
composer test
Lint with
composer lint
The project parses PHPStorm's PHP stubs to get support for PHP builtins. It re-parses them as needed after Composer processes, but after some code changes (such as ones involving the index or parsing) you may have to explicitly re-parse them:
composer run-script parse-stubs
To debug with xDebug ensure that you have this set as an environment variable
PHPLS_ALLOW_XDEBUG=1

View File

@ -30,21 +30,25 @@ $iterator = new RecursiveDirectoryIterator(__DIR__ . "/../validation/frameworks/
$testProviderArray = array();
foreach (new RecursiveIteratorIterator($iterator) as $file) {
if (strpos((string)$file, ".php") !== false) {
if (strpos((string) $file, ".php") !== false) {
$totalSize += $file->getSize();
$testProviderArray[] = $file->getRealPath();
}
}
if (count($testProviderArray) === 0) {
throw new Exception("ERROR: Validation testsuite frameworks not found - run `git submodule update --init --recursive` to download.");
throw new Exception(
"ERROR: Validation testsuite frameworks not found - run `git submodule update --init --recursive` to download."
);
}
$index = new Index;
$index = new Index();
$definitionResolver = new DefinitionResolver($index);
$completionProvider = new CompletionProvider($definitionResolver, $index);
$docBlockFactory = DocBlockFactory::createInstance();
$completionFile = realpath(__DIR__ . '/../validation/frameworks/symfony/src/Symfony/Component/HttpFoundation/Request.php');
$completionFile = realpath(
__DIR__ . '/../validation/frameworks/symfony/src/Symfony/Component/HttpFoundation/Request.php'
);
$parser = new PhpParser\Parser();
$completionDocument = null;
@ -71,7 +75,7 @@ foreach ($testProviderArray as $idx => $testCaseFile) {
}
}
echo "Getting completion". PHP_EOL;
echo "Getting completion" . PHP_EOL;
// Completion in $this->|request = new ParameterBag($request);
$start = microtime(true);

View File

@ -22,21 +22,32 @@ unset($xdebugHandler);
$totalSize = 0;
$frameworks = ["drupal", "wordpress", "php-language-server", "tolerant-php-parser", "math-php", "symfony", "codeigniter", "cakephp"];
$frameworks = [
"drupal",
"wordpress",
"php-language-server",
"tolerant-php-parser",
"math-php",
"symfony",
"codeigniter",
"cakephp"
];
foreach($frameworks as $framework) {
foreach ($frameworks as $framework) {
$iterator = new RecursiveDirectoryIterator(__DIR__ . "/../validation/frameworks/$framework");
$testProviderArray = array();
foreach (new RecursiveIteratorIterator($iterator) as $file) {
if (strpos((string)$file, ".php") !== false) {
if (strpos((string) $file, ".php") !== false) {
$totalSize += $file->getSize();
$testProviderArray[] = $file->getPathname();
}
}
if (count($testProviderArray) === 0) {
throw new Exception("ERROR: Validation testsuite frameworks not found - run `git submodule update --init --recursive` to download.");
throw new Exception(
"ERROR: Validation testsuite frameworks not found - run `git submodule update --init --recursive` to download."
);
}
$start = microtime(true);
@ -52,18 +63,24 @@ foreach($frameworks as $framework) {
$fileContents = file_get_contents($testCaseFile);
$docBlockFactory = DocBlockFactory::createInstance();
$index = new Index;
$index = new Index();
$maxRecursion = [];
$definitions = [];
$definitionResolver = new DefinitionResolver($index);
$parser = new PhpParser\Parser();
$document = new PhpDocument($testCaseFile, $fileContents, $index, $parser, $docBlockFactory, $definitionResolver);
$document = new PhpDocument(
$testCaseFile,
$fileContents,
$index,
$parser,
$docBlockFactory,
$definitionResolver
);
}
echo "------------------------------\n";
echo "Time [$framework]: " . (microtime(true) - $start) . PHP_EOL;
}

View File

@ -8,7 +8,10 @@ $options = getopt('', ['tcp::', 'tcp-server::', 'memory-limit::']);
ini_set('memory_limit', $options['memory-limit'] ?? '4G');
foreach ([__DIR__ . '/../../../autoload.php', __DIR__ . '/../autoload.php', __DIR__ . '/../vendor/autoload.php'] as $file) {
foreach (
[__DIR__ . '/../../../autoload.php', __DIR__ . '/../autoload.php', __DIR__ . '/../vendor/autoload.php']
as $file
) {
if (file_exists($file)) {
require $file;
break;
@ -28,7 +31,7 @@ $logger = new StderrLogger();
// Only write uncaught exceptions to STDERR, not STDOUT
set_exception_handler(function (\Throwable $e) use ($logger) {
$logger->critical((string)$e);
$logger->critical((string) $e);
});
@cli_set_process_title('PHP Language Server');
@ -48,12 +51,9 @@ if (!empty($options['tcp'])) {
exit(1);
}
stream_set_blocking($socket, false);
$ls = new LanguageServer(
new ProtocolStreamReader($socket),
new ProtocolStreamWriter($socket)
);
$ls = new LanguageServer(new ProtocolStreamReader($socket), new ProtocolStreamWriter($socket));
Loop\run();
} else if (!empty($options['tcp-server'])) {
} elseif (!empty($options['tcp-server'])) {
// Run a TCP Server
$address = $options['tcp-server'];
$tcpServer = stream_socket_server('tcp://' . $address, $errno, $errstr);
@ -66,7 +66,7 @@ if (!empty($options['tcp'])) {
if (!$pcntlAvailable) {
$logger->notice('PCNTL is not available. Only a single connection will be accepted');
}
while ($socket = stream_socket_accept($tcpServer, -1)) {
while (($socket = stream_socket_accept($tcpServer, -1))) {
$logger->debug('Connection accepted');
stream_set_blocking($socket, false);
if ($pcntlAvailable) {
@ -76,7 +76,7 @@ if (!empty($options['tcp'])) {
if ($pid === -1) {
$logger->critical('Could not fork');
exit(1);
} else if ($pid === 0) {
} elseif ($pid === 0) {
// Child process
$reader = new ProtocolStreamReader($socket);
$writer = new ProtocolStreamWriter($socket);
@ -91,10 +91,7 @@ if (!empty($options['tcp'])) {
} 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)
);
$ls = new LanguageServer(new ProtocolStreamReader($socket), new ProtocolStreamWriter($socket));
Loop\run();
}
}
@ -102,9 +99,6 @@ if (!empty($options['tcp'])) {
// Use STDIO
$logger->debug('Listening on STDIN');
stream_set_blocking(STDIN, false);
$ls = new LanguageServer(
new ProtocolStreamReader(STDIN),
new ProtocolStreamWriter(STDOUT)
);
$ls = new LanguageServer(new ProtocolStreamReader(STDIN), new ProtocolStreamWriter(STDOUT));
Loop\run();
}

View File

@ -1,4 +1,3 @@
coverage:
status:
project:
@ -8,9 +7,9 @@ coverage:
base: auto
comment:
layout: "header, diff, tree, changes"
layout: 'header, diff, tree, changes'
behavior: default
require_changes: false # if true: only post the comment if coverage changes
require_changes: false # if true: only post the comment if coverage changes
branches: null
flags: null
paths: null

View File

@ -1,17 +1,16 @@
collectors:
# pull requests for new major versions
- type: php-composer
path: /
actors:
# pull requests for new major versions
- type: php-composer
versions: "Y.0.0"
settings:
commit_message_prefix: "chore: "
- type: js-npm
path: /
actors:
path: /
actors:
- type: php-composer
versions: 'Y.0.0'
settings:
commit_message_prefix: 'chore: '
- type: js-npm
versions: "Y.0.0"
settings:
commit_message_prefix: "chore: "
path: /
actors:
- type: js-npm
versions: 'Y.0.0'
settings:
commit_message_prefix: 'chore: '

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Cache;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Cache;
@ -32,9 +32,12 @@ class ClientCache implements Cache
*/
public function get(string $key): Promise
{
return $this->client->xcache->get($key)->then('unserialize')->otherwise(function () {
// Ignore
});
return $this->client->xcache
->get($key)
->then('unserialize')
->otherwise(function () {
// Ignore
});
}
/**

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Cache;
@ -19,7 +19,7 @@ class FileSystemCache implements Cache
{
if (strtoupper(substr(php_uname('s'), 0, 3)) === 'WIN') {
$this->cacheDir = getenv('LOCALAPPDATA') . '\\PHP Language Server\\';
} else if (getenv('XDG_CACHE_HOME')) {
} elseif (getenv('XDG_CACHE_HOME')) {
$this->cacheDir = getenv('XDG_CACHE_HOME') . '/phpls/';
} else {
$this->cacheDir = getenv('HOME') . '/.phpls/';

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Client;
@ -53,11 +53,12 @@ class TextDocument
*/
public function xcontent(TextDocumentIdentifier $textDocument): Promise
{
return $this->handler->request(
'textDocument/xcontent',
['textDocument' => $textDocument]
)->then(function ($result) {
return $this->mapper->map($result, new TextDocumentItem);
});
return $this->handler
->request('textDocument/xcontent', [
'textDocument' => $textDocument
])
->then(function ($result) {
return $this->mapper->map($result, new TextDocumentItem());
});
}
}

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Client;
@ -31,7 +31,10 @@ class Window
*/
public function showMessage(int $type, string $message): Promise
{
return $this->handler->notify('window/showMessage', ['type' => $type, 'message' => $message]);
return $this->handler->notify('window/showMessage', [
'type' => $type,
'message' => $message
]);
}
/**
@ -43,6 +46,9 @@ class Window
*/
public function logMessage(int $type, string $message): Promise
{
return $this->handler->notify('window/logMessage', ['type' => $type, 'message' => $message]);
return $this->handler->notify('window/logMessage', [
'type' => $type,
'message' => $message
]);
}
}

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Client;
@ -37,10 +37,7 @@ class Workspace
*/
public function xfiles(string $base = null): Promise
{
return $this->handler->request(
'workspace/xfiles',
['base' => $base]
)->then(function (array $textDocuments) {
return $this->handler->request('workspace/xfiles', ['base' => $base])->then(function (array $textDocuments) {
return $this->mapper->mapArray($textDocuments, [], TextDocumentIdentifier::class);
});
}

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Client;
@ -37,6 +37,9 @@ class XCache
*/
public function set(string $key, $value): Promise
{
return $this->handler->notify('xcache/set', ['key' => $key, 'value' => $value]);
return $this->handler->notify('xcache/set', [
'key' => $key,
'value' => $value
]);
}
}

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer;
@ -27,7 +27,7 @@ class ClientHandler
{
$this->protocolReader = $protocolReader;
$this->protocolWriter = $protocolWriter;
$this->idGenerator = new IdGenerator;
$this->idGenerator = new IdGenerator();
}
/**
@ -40,26 +40,24 @@ class ClientHandler
public function request(string $method, $params): Promise
{
$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) {
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);
} else {
$promise->reject($msg->body->error);
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) {
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);
} else {
$promise->reject($msg->body->error);
}
}
}
};
$this->protocolReader->on('message', $listener);
return $promise;
});
};
$this->protocolReader->on('message', $listener);
return $promise;
});
}
/**
@ -71,10 +69,6 @@ class ClientHandler
*/
public function notify(string $method, $params): Promise
{
return $this->protocolWriter->write(
new Message(
new AdvancedJsonRpc\Notification($method, (object)$params)
)
);
return $this->protocolWriter->write(new Message(new AdvancedJsonRpc\Notification($method, (object) $params)));
}
}

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer;
@ -116,7 +116,7 @@ class CompletionProvider
'from', // As in yield from
'strict_types',
'ticks', // As in declare(ticks=1)
'encoding', // As in declare(encoding='EBCDIC')
'encoding' // As in declare(encoding='EBCDIC')
];
/**
@ -163,18 +163,19 @@ class CompletionProvider
// Get the node at the position under the cursor
$offset = $node === null ? -1 : $pos->toOffset($node->getFileContents());
if (
$node !== null
&& $offset > $node->getEndPosition()
&& $node->parent !== null
&& $node->parent->getLastChild() instanceof PhpParser\MissingToken
$node !== null &&
$offset > $node->getEndPosition() &&
$node->parent !== null &&
$node->parent->getLastChild() instanceof PhpParser\MissingToken
) {
$node = $node->parent;
}
$list = new CompletionList;
$list = new CompletionList();
$list->isIncomplete = true;
if ($node instanceof Node\Expression\Variable &&
if (
$node instanceof Node\Expression\Variable &&
$node->parent instanceof Node\Expression\ObjectCreationExpression &&
$node->name instanceof PhpParser\MissingToken
) {
@ -186,19 +187,13 @@ class CompletionProvider
$content = $doc->getContent();
$offset = $pos->toOffset($content);
if (
$node === null
|| (
$node instanceof Node\Statement\InlineHtml
&& (
$context !== null
// Make sure to not suggest on the > trigger character in HTML
&& (
$context->triggerKind === CompletionTriggerKind::INVOKED
|| $context->triggerCharacter === '<'
)
)
)
|| $pos == new Position(0, 0)
// Make sure to not suggest on the > trigger character in HTML
$node === null ||
($node instanceof Node\Statement\InlineHtml &&
($context !== null &&
($context->triggerKind === CompletionTriggerKind::INVOKED ||
$context->triggerCharacter === '<'))) ||
$pos == new Position(0, 0)
) {
// HTML, beginning of file
@ -209,12 +204,11 @@ class CompletionProvider
stripStringOverlap($doc->getRange(new Range(new Position(0, 0), $pos)), '<?php')
);
$list->items[] = $item;
} elseif (
$node instanceof Node\Expression\Variable
&& !(
$node->parent instanceof Node\Expression\ScopedPropertyAccessExpression
&& $node->parent->memberName === $node
$node instanceof Node\Expression\Variable &&
!(
$node->parent instanceof Node\Expression\ScopedPropertyAccessExpression &&
$node->parent->memberName === $node
)
) {
// Variables
@ -225,18 +219,17 @@ class CompletionProvider
// Find variables, parameters and use statements in the scope
$namePrefix = $node->getName() ?? '';
foreach ($this->suggestVariablesAtNode($node, $namePrefix) as $var) {
$item = new CompletionItem;
$item = new CompletionItem();
$item->kind = CompletionItemKind::VARIABLE;
$item->label = '$' . $var->getName();
$item->documentation = $this->definitionResolver->getDocumentationFromNode($var);
$item->detail = (string)$this->definitionResolver->getTypeFromNode($var);
$item->detail = (string) $this->definitionResolver->getTypeFromNode($var);
$item->textEdit = new TextEdit(
new Range($pos, $pos),
stripStringOverlap($doc->getRange(new Range(new Position(0, 0), $pos)), $item->label)
);
$list->items[] = $item;
}
} elseif ($node instanceof Node\Expression\MemberAccessExpression) {
// Member access expressions
//
@ -260,7 +253,6 @@ class CompletionProvider
}
}
}
} elseif (
($scoped = $node->parent) instanceof Node\Expression\ScopedPropertyAccessExpression ||
($scoped = $node) instanceof Node\Expression\ScopedPropertyAccessExpression
@ -276,7 +268,7 @@ class CompletionProvider
// Resolve all possible types to FQNs
$fqns = FqnUtilities\getFqnsFromType(
$classType = $this->definitionResolver->resolveExpressionNodeToType($scoped->scopeResolutionQualifier)
($classType = $this->definitionResolver->resolveExpressionNodeToType($scoped->scopeResolutionQualifier))
);
// The FQNs of the symbol and its parents (eg the implemented interfaces)
@ -291,12 +283,11 @@ class CompletionProvider
}
}
}
} elseif (
ParserHelpers\isConstantFetch($node)
// Creation gets set in case of an instantiation (`new` expression)
|| ($creation = $node->parent) instanceof Node\Expression\ObjectCreationExpression
|| (($creation = $node) instanceof Node\Expression\ObjectCreationExpression)
ParserHelpers\isConstantFetch($node) ||
($creation = $node->parent) instanceof Node\Expression\ObjectCreationExpression ||
($creation = $node) instanceof Node\Expression\ObjectCreationExpression
) {
// Class instantiations, function calls, constant fetches, class names
//
@ -311,7 +302,10 @@ class CompletionProvider
if ($nameNode instanceof Node\QualifiedName) {
/** @var string The typed name. */
$prefix = (string)PhpParser\ResolvedName::buildName($nameNode->nameParts, $nameNode->getFileContents());
$prefix = (string) PhpParser\ResolvedName::buildName(
$nameNode->nameParts,
$nameNode->getFileContents()
);
} else {
$prefix = $nameNode->getText($node->getFileContents());
}
@ -341,7 +335,7 @@ class CompletionProvider
// \Prefix\Goes\Here| - Only return completions from the root namespace.
/** @var $items \Generator|CompletionItem[] Generator yielding CompletionItems indexed by their FQN */
$items = $this->getCompletionsForFqnPrefix($prefix, $isCreation, false);
} else if ($isQualified) {
} elseif ($isQualified) {
// Prefix\Goes\Here|
$items = $this->getPartiallyQualifiedCompletions(
$prefix,
@ -361,7 +355,6 @@ class CompletionProvider
$item->insertText = substr($item->insertText, 0, -2);
}
}
}
return $list;
}
@ -374,13 +367,13 @@ class CompletionProvider
): \Generator {
// If the first part of the partially qualified name matches a namespace alias,
// only definitions below that alias can be completed.
list($namespaceAliases,,) = $importTables;
list($namespaceAliases, ,) = $importTables;
$prefixFirstPart = nameGetFirstPart($prefix);
$foundAlias = $foundAliasFqn = null;
foreach ($namespaceAliases as $alias => $aliasFqn) {
if (strcasecmp($prefixFirstPart, $alias) === 0) {
$foundAlias = $alias;
$foundAliasFqn = (string)$aliasFqn;
$foundAliasFqn = (string) $aliasFqn;
break;
}
}
@ -420,20 +413,16 @@ class CompletionProvider
bool $requireCanBeInstantiated
): \Generator {
// Aliases
list($namespaceAliases,,) = $importTables;
list($namespaceAliases, ,) = $importTables;
// use Foo\Bar
yield from $this->getCompletionsForAliases(
$prefix,
$namespaceAliases,
$requireCanBeInstantiated
);
yield from $this->getCompletionsForAliases($prefix, $namespaceAliases, $requireCanBeInstantiated);
// Completions from the current namespace
yield from $this->getCompletionsForFqnPrefix(
nameConcat($currentNamespace, $prefix),
$requireCanBeInstantiated,
false
);
nameConcat($currentNamespace, $prefix),
$requireCanBeInstantiated,
false
);
if ($currentNamespace !== '' && $prefix === '') {
// Get additional suggestions from the global namespace.
@ -477,7 +466,7 @@ class CompletionProvider
}
$completion = CompletionItemFactory::fromDefinition($def);
if ($insertFullyQualified) {
$completion->insertText = '\\' . $fqn;
$completion->insertText = '\\' . $fqn;
}
yield $fqn => $completion;
}
@ -500,14 +489,14 @@ class CompletionProvider
if (!nameStartsWith($alias, $prefix)) {
continue;
}
$definition = $this->index->getDefinition((string)$aliasFqn);
$definition = $this->index->getDefinition((string) $aliasFqn);
if ($definition) {
if ($requireCanBeInstantiated && !$definition->canBeInstantiated) {
continue;
}
$completionItem = CompletionItemFactory::fromDefinition($definition);
$completionItem->insertText = $alias;
yield (string)$aliasFqn => $completionItem;
yield (string) $aliasFqn => $completionItem;
}
}
}
@ -527,11 +516,7 @@ class CompletionProvider
$prefixFirstPart = nameGetFirstPart($prefix);
// Matched alias.
$resolvedPrefix = nameConcat($aliasFqn, nameWithoutFirstPart($prefix));
$completionItems = $this->getCompletionsForFqnPrefix(
$resolvedPrefix,
$requireCanBeInstantiated,
false
);
$completionItems = $this->getCompletionsForFqnPrefix($resolvedPrefix, $requireCanBeInstantiated, false);
// Convert FQNs in the CompletionItems so they are expressed in terms of the alias.
foreach ($completionItems as $fqn => $completionItem) {
/** @var string $fqn with the leading parts determined by the alias removed. Has the leading backslash. */
@ -556,7 +541,7 @@ class CompletionProvider
$completionItem = CompletionItemFactory::fromDefinition($def);
// Second-guessing the user here - do not trust roaming to work. If the same symbol is
// inserted in the current namespace, the code will stop working.
$completionItem->insertText = '\\' . $fqn;
$completionItem->insertText = '\\' . $fqn;
yield $fqn => $completionItem;
}
}
@ -584,7 +569,7 @@ class CompletionProvider
* @param string[] $fqns
* @return Generator
*/
private function expandParentFqns(array $fqns) : Generator
private function expandParentFqns(array $fqns): Generator
{
foreach ($fqns as $fqn) {
yield $fqn;
@ -625,7 +610,7 @@ class CompletionProvider
while ($level && !($level instanceof PhpParser\FunctionLike)) {
// Walk siblings before the node
$sibling = $level;
while ($sibling = $sibling->getPreviousSibling()) {
while (($sibling = $sibling->getPreviousSibling())) {
// Collect all variables inside the sibling node
foreach ($this->findVariableDefinitionsInNode($sibling, $namePrefix) as $var) {
$vars[$var->getName()] = $var;
@ -644,9 +629,11 @@ class CompletionProvider
}
}
if ($level instanceof Node\Expression\AnonymousFunctionCreationExpression
&& $level->anonymousFunctionUseClause !== null
&& $level->anonymousFunctionUseClause->useVariableNameList !== null) {
if (
$level instanceof Node\Expression\AnonymousFunctionCreationExpression &&
$level->anonymousFunctionUseClause !== null &&
$level->anonymousFunctionUseClause->useVariableNameList !== null
) {
foreach ($level->anonymousFunctionUseClause->useVariableNameList->getValues() as $use) {
$useName = $use->getName();
if (empty($namePrefix) || strpos($useName, $namePrefix) !== false) {
@ -679,8 +666,9 @@ class CompletionProvider
$vars[] = $node->leftOperand;
} elseif ($node instanceof Node\ForeachKey || $node instanceof Node\ForeachValue) {
foreach ($node->getDescendantNodes() as $descendantNode) {
if ($descendantNode instanceof Node\Expression\Variable
&& ($namePrefix === '' || strpos($descendantNode->getName(), $namePrefix) !== false)
if (
$descendantNode instanceof Node\Expression\Variable &&
($namePrefix === '' || strpos($descendantNode->getName(), $namePrefix) !== false)
) {
$vars[] = $descendantNode;
}
@ -700,8 +688,8 @@ class CompletionProvider
private function isAssignmentToVariableWithPrefix(Node $node, string $namePrefix): bool
{
return $node instanceof Node\Expression\AssignmentExpression
&& $node->leftOperand instanceof Node\Expression\Variable
&& ($namePrefix === '' || strpos($node->leftOperand->getName(), $namePrefix) !== false);
return $node instanceof Node\Expression\AssignmentExpression &&
$node->leftOperand instanceof Node\Expression\Variable &&
($namePrefix === '' || strpos($node->leftOperand->getName(), $namePrefix) !== false);
}
}

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer;
@ -12,7 +12,10 @@ use Sabre\Uri;
use function Sabre\Event\coroutine;
use Microsoft\PhpParser;
foreach ([__DIR__ . '/../../../autoload.php', __DIR__ . '/../autoload.php', __DIR__ . '/../vendor/autoload.php'] as $file) {
foreach (
[__DIR__ . '/../../../autoload.php', __DIR__ . '/../autoload.php', __DIR__ . '/../vendor/autoload.php']
as $file
) {
if (file_exists($file)) {
require $file;
break;
@ -23,18 +26,22 @@ class ComposerScripts
{
public static function parseStubs()
{
// Change URI to phpstubs://
// Create a new document and add it to $index
coroutine(function () {
$index = new StubsIndex();
$index = new StubsIndex;
$finder = new FileSystemFilesFinder;
$contentRetriever = new FileSystemContentRetriever;
$finder = new FileSystemFilesFinder();
$contentRetriever = new FileSystemContentRetriever();
$docBlockFactory = DocBlockFactory::createInstance();
$parser = new PhpParser\Parser();
$definitionResolver = new DefinitionResolver($index);
$stubsLocation = null;
foreach ([__DIR__ . '/../../../jetbrains/phpstorm-stubs', __DIR__ . '/../vendor/jetbrains/phpstorm-stubs'] as $dir) {
foreach (
[__DIR__ . '/../../../jetbrains/phpstorm-stubs', __DIR__ . '/../vendor/jetbrains/phpstorm-stubs']
as $dir
) {
if (file_exists($dir)) {
$stubsLocation = Path::canonicalize($dir);
break;
@ -44,19 +51,17 @@ class ComposerScripts
throw new \Exception('jetbrains/phpstorm-stubs package not found');
}
$uris = yield $finder->find("$stubsLocation/**/*.php");
$uris = (yield $finder->find("$stubsLocation/**/*.php"));
foreach ($uris as $uri) {
echo "Parsing $uri\n";
$content = yield $contentRetriever->retrieve($uri);
$content = (yield $contentRetriever->retrieve($uri));
// Change URI to phpstubs://
$parts = Uri\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);
}

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\ContentRetriever;
@ -28,7 +28,8 @@ class ClientContentRetriever implements ContentRetriever
*/
public function retrieve(string $uri): Promise
{
return $this->client->textDocument->xcontent(new TextDocumentIdentifier($uri))
return $this->client->textDocument
->xcontent(new TextDocumentIdentifier($uri))
->then(function (TextDocumentItem $textDocument) {
return $textDocument->text;
});

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\ContentRetriever;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\ContentRetriever;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer;
@ -9,9 +9,7 @@ use LanguageServerProtocol\SymbolInformation;
use Microsoft\PhpParser;
use Microsoft\PhpParser\Node;
use Microsoft\PhpParser\FunctionLike;
use phpDocumentor\Reflection\{
DocBlock, DocBlockFactory, Fqsen, Type, TypeResolver, Types
};
use phpDocumentor\Reflection\{DocBlock, DocBlockFactory, Fqsen, Type, TypeResolver, Types};
class DefinitionResolver
{
@ -49,7 +47,7 @@ class DefinitionResolver
public function __construct(ReadableIndex $index)
{
$this->index = $index;
$this->typeResolver = new TypeResolver;
$this->typeResolver = new TypeResolver();
$this->docBlockFactory = DocBlockFactory::createInstance();
$this->signatureInformationFactory = new SignatureInformationFactory($this);
}
@ -66,8 +64,10 @@ class DefinitionResolver
// - [PropertyDeclaration] // public $a, [$b = 3], $c; => public $b = 3;
// - [ConstDeclaration|ClassConstDeclaration] // "const A = 3, [B = 4];" => "const B = 4;"
if (
($declaration = ParserHelpers\tryGetPropertyDeclaration($node)) && ($elements = $declaration->propertyElements) ||
($declaration = ParserHelpers\tryGetConstOrClassConstDeclaration($node)) && ($elements = $declaration->constElements)
(($declaration = ParserHelpers\tryGetPropertyDeclaration($node)) &&
($elements = $declaration->propertyElements)) ||
(($declaration = ParserHelpers\tryGetConstOrClassConstDeclaration($node)) &&
($elements = $declaration->constElements))
) {
$defLine = $declaration->getText();
$defLineStart = $declaration->getStart();
@ -105,7 +105,8 @@ class DefinitionResolver
// For properties and constants, set the node to the declaration node, rather than the individual property.
// This is because they get defined as part of a list.
$constOrPropertyDeclaration = ParserHelpers\tryGetPropertyDeclaration($node) ?? ParserHelpers\tryGetConstOrClassConstDeclaration($node);
$constOrPropertyDeclaration =
ParserHelpers\tryGetPropertyDeclaration($node) ?? ParserHelpers\tryGetConstOrClassConstDeclaration($node);
if ($constOrPropertyDeclaration !== null) {
$node = $constOrPropertyDeclaration;
}
@ -148,13 +149,13 @@ class DefinitionResolver
// TODO make more efficient (caching, ensure import table is in right format to begin with)
$docCommentText = $node->getDocCommentText();
if ($docCommentText !== null) {
list($namespaceImportTable,,) = $node->getImportTablesForCurrentScope();
list($namespaceImportTable, ,) = $node->getImportTablesForCurrentScope();
foreach ($namespaceImportTable as $alias => $name) {
$namespaceImportTable[$alias] = (string)$name;
$namespaceImportTable[$alias] = (string) $name;
}
$namespaceDefinition = $node->getNamespaceDefinition();
if ($namespaceDefinition !== null && $namespaceDefinition->name !== null) {
$namespaceName = (string)$namespaceDefinition->name->getNamespacedName();
$namespaceName = (string) $namespaceDefinition->name->getNamespacedName();
} else {
$namespaceName = 'global';
}
@ -180,57 +181,54 @@ class DefinitionResolver
*/
public function createDefinitionFromNode(Node $node, string $fqn = null): Definition
{
$def = new Definition;
$def = new Definition();
$def->fqn = $fqn;
// Determines whether the suggestion will show after "new"
$def->canBeInstantiated = (
$node instanceof Node\Statement\ClassDeclaration &&
// check whether it is not an abstract class
($node->abstractOrFinalModifier === null || $node->abstractOrFinalModifier->kind !== PhpParser\TokenKind::AbstractKeyword)
);
$def->canBeInstantiated =
$node instanceof
Node\Statement\ClassDeclaration &&
// check whether it is not an abstract class
($node->abstractOrFinalModifier === null ||
$node->abstractOrFinalModifier->kind !== PhpParser\TokenKind::AbstractKeyword);
// Interfaces, classes, traits, namespaces, functions, and global const elements
$def->isMember = !(
$node instanceof PhpParser\ClassLike ||
($node instanceof Node\Statement\NamespaceDefinition && $node->name !== null) ||
$node instanceof Node\Statement\FunctionDeclaration ||
($node instanceof Node\ConstElement && $node->parent->parent instanceof Node\Statement\ConstDeclaration)
);
// Definition is affected by global namespace fallback if it is a global constant or a global function
$def->roamed = (
$fqn !== null
&& strpos($fqn, '\\') === false
&& (
($node instanceof Node\ConstElement && $node->parent->parent instanceof Node\Statement\ConstDeclaration)
|| $node instanceof Node\Statement\FunctionDeclaration
)
);
$def->roamed =
$fqn !== null &&
strpos($fqn, '\\') === false &&
(($node instanceof Node\ConstElement && $node->parent->parent instanceof Node\Statement\ConstDeclaration) ||
$node instanceof Node\Statement\FunctionDeclaration);
// Static methods and static property declarations
$def->isStatic = (
$def->isStatic =
($node instanceof Node\MethodDeclaration && $node->isStatic()) ||
(($propertyDeclaration = ParserHelpers\tryGetPropertyDeclaration($node)) !== null &&
$propertyDeclaration->isStatic());
(($propertyDeclaration = ParserHelpers\tryGetPropertyDeclaration($node)) !== null
&& $propertyDeclaration->isStatic())
);
if ($node instanceof Node\Statement\ClassDeclaration &&
if (
// TODO - this should be better represented in the parser API
$node->classBaseClause !== null && $node->classBaseClause->baseClass !== null) {
$def->extends = [(string)$node->classBaseClause->baseClass->getResolvedName()];
$node instanceof Node\Statement\ClassDeclaration &&
$node->classBaseClause !== null &&
$node->classBaseClause->baseClass !== null
) {
$def->extends = [(string) $node->classBaseClause->baseClass->getResolvedName()];
} elseif (
$node instanceof Node\Statement\InterfaceDeclaration &&
// TODO - this should be better represented in the parser API
$node->interfaceBaseClause !== null && $node->interfaceBaseClause->interfaceNameList !== null
$node instanceof Node\Statement\InterfaceDeclaration &&
$node->interfaceBaseClause !== null &&
$node->interfaceBaseClause->interfaceNameList !== null
) {
$def->extends = [];
foreach ($node->interfaceBaseClause->interfaceNameList->getValues() as $n) {
$def->extends[] = (string)$n->getResolvedName();
$def->extends[] = (string) $n->getResolvedName();
}
}
@ -261,10 +259,12 @@ class DefinitionResolver
// Variables are not indexed globally, as they stay in the file scope anyway.
// Ignore variable nodes that are part of ScopedPropertyAccessExpression,
// as the scoped property access expression node is handled separately.
if ($node instanceof Node\Expression\Variable &&
!($parent instanceof Node\Expression\ScopedPropertyAccessExpression)) {
if (
$node instanceof Node\Expression\Variable &&
!($parent instanceof Node\Expression\ScopedPropertyAccessExpression)
) {
// Resolve $this to the containing class definition.
if ($node->getName() === 'this' && $fqn = $this->getContainingClassFqn($node)) {
if ($node->getName() === 'this' && ($fqn = $this->getContainingClassFqn($node))) {
return $this->index->getDefinition($fqn, false);
}
@ -289,17 +289,17 @@ class DefinitionResolver
if (!$classNode) {
return;
}
$fqn = (string)$classNode->getNamespacedName();
$fqn = (string) $classNode->getNamespacedName();
if (!$fqn) {
return;
}
} else if ($fqn === 'parent') {
} elseif ($fqn === 'parent') {
// Resolve parent keyword to the base class FQN
$classNode = $node->getFirstAncestor(Node\Statement\ClassDeclaration::class);
if (!$classNode || !$classNode->classBaseClause || !$classNode->classBaseClause->baseClass) {
return;
}
$fqn = (string)$classNode->classBaseClause->baseClass->getResolvedName();
$fqn = (string) $classNode->classBaseClause->baseClass->getResolvedName();
if (!$fqn) {
return;
}
@ -327,17 +327,17 @@ class DefinitionResolver
// TODO all name tokens should be a part of a node
if ($node instanceof Node\QualifiedName) {
return $this->resolveQualifiedNameNodeToFqn($node);
} else if ($node instanceof Node\Expression\MemberAccessExpression) {
} elseif ($node instanceof Node\Expression\MemberAccessExpression) {
return $this->resolveMemberAccessExpressionNodeToFqn($node);
} else if (ParserHelpers\isConstantFetch($node)) {
return (string)($node->getNamespacedName());
} else if (
} elseif (ParserHelpers\isConstantFetch($node)) {
return (string) $node->getNamespacedName();
} elseif (
// A\B::C - constant access expression
$node instanceof Node\Expression\ScopedPropertyAccessExpression
&& !($node->memberName instanceof Node\Expression\Variable)
$node instanceof Node\Expression\ScopedPropertyAccessExpression &&
!($node->memberName instanceof Node\Expression\Variable)
) {
return $this->resolveScopedPropertyAccessExpressionNodeToFqn($node);
} else if (
} elseif (
// A\B::$c - static property access expression
$node->parent instanceof Node\Expression\ScopedPropertyAccessExpression
) {
@ -355,8 +355,9 @@ class DefinitionResolver
return null;
}
// Add use clause references
if (($useClause = $parent) instanceof Node\NamespaceUseGroupClause
|| $useClause instanceof Node\NamespaceUseClause
if (
($useClause = $parent) instanceof Node\NamespaceUseGroupClause ||
$useClause instanceof Node\NamespaceUseClause
) {
$contents = $node->getFileContents();
if ($useClause instanceof Node\NamespaceUseGroupClause) {
@ -366,18 +367,25 @@ class DefinitionResolver
}
$name = PhpParser\ResolvedName::buildName($prefix->nameParts, $contents);
$name->addNameParts($node->nameParts, $contents);
$name = (string)$name;
$name = (string) $name;
if ($useClause->functionOrConst === null) {
$useClause = $node->getFirstAncestor(Node\Statement\NamespaceUseDeclaration::class);
if ($useClause->functionOrConst !== null && $useClause->functionOrConst->kind === PhpParser\TokenKind::FunctionKeyword) {
if (
$useClause->functionOrConst !== null &&
$useClause->functionOrConst->kind === PhpParser\TokenKind::FunctionKeyword
) {
$name .= '()';
}
}
return $name;
} else {
$name = (string) PhpParser\ResolvedName::buildName($node->nameParts, $contents);
if ($useClause->groupClauses === null && $useClause->parent->parent->functionOrConst !== null && $useClause->parent->parent->functionOrConst->kind === PhpParser\TokenKind::FunctionKeyword) {
if (
$useClause->groupClauses === null &&
$useClause->parent->parent->functionOrConst !== null &&
$useClause->parent->parent->functionOrConst->kind === PhpParser\TokenKind::FunctionKeyword
) {
$name .= '()';
}
}
@ -406,32 +414,30 @@ class DefinitionResolver
if ($varType instanceof Types\Compound) {
// For compound types, use the first FQN we find
// (popular use case is ClassName|null)
for ($i = 0; $t = $varType->get($i); $i++) {
for ($i = 0; ($t = $varType->get($i)); $i++) {
if (
$t instanceof Types\This
|| $t instanceof Types\Object_
|| $t instanceof Types\Static_
|| $t instanceof Types\Self_
$t instanceof Types\This ||
$t instanceof Types\Object_ ||
$t instanceof Types\Static_ ||
$t instanceof Types\Self_
) {
$varType = $t;
break;
}
}
}
if (
$varType instanceof Types\This
|| $varType instanceof Types\Static_
|| $varType instanceof Types\Self_
) {
if ($varType instanceof Types\This || $varType instanceof Types\Static_ || $varType instanceof Types\Self_) {
// $this/static/self is resolved to the containing class
$classFqn = self::getContainingClassFqn($access);
} else if (!($varType instanceof Types\Object_) || $varType->getFqsen() === null) {
} elseif (!($varType instanceof Types\Object_) || $varType->getFqsen() === null) {
// Left-hand expression could not be resolved to a class
return null;
} else {
$classFqn = substr((string)$varType->getFqsen(), 1);
$classFqn = substr((string) $varType->getFqsen(), 1);
}
$memberSuffix = '->' . (string)($access->memberName->getText() ?? $access->memberName->getText($access->getFileContents()));
$memberSuffix =
'->' .
(string) ($access->memberName->getText() ?? $access->memberName->getText($access->getFileContents()));
if ($access->parent instanceof Node\Expression\CallExpression) {
$memberSuffix .= '()';
}
@ -440,7 +446,7 @@ class DefinitionResolver
$implementorFqns = [$classFqn];
$visitedFqns = [];
while ($implementorFqn = array_shift($implementorFqns)) {
while (($implementorFqn = array_shift($implementorFqns))) {
// If the member FQN exists, return it
if ($this->index->getDefinition($implementorFqn . $memberSuffix)) {
return $implementorFqn . $memberSuffix;
@ -467,16 +473,17 @@ class DefinitionResolver
return $classFqn . $memberSuffix;
}
private function resolveScopedPropertyAccessExpressionNodeToFqn(Node\Expression\ScopedPropertyAccessExpression $scoped)
{
private function resolveScopedPropertyAccessExpressionNodeToFqn(
Node\Expression\ScopedPropertyAccessExpression $scoped
) {
if ($scoped->scopeResolutionQualifier instanceof Node\Expression\Variable) {
$varType = $this->getTypeFromNode($scoped->scopeResolutionQualifier);
if ($varType === null) {
return null;
}
$className = substr((string)$varType->getFqsen(), 1);
$className = substr((string) $varType->getFqsen(), 1);
} elseif ($scoped->scopeResolutionQualifier instanceof Node\QualifiedName) {
$className = (string)$scoped->scopeResolutionQualifier->getResolvedName();
$className = (string) $scoped->scopeResolutionQualifier->getResolvedName();
} else {
return null;
}
@ -492,9 +499,9 @@ class DefinitionResolver
if (!isset($classNode->extends)) {
return null;
}
$className = (string)$classNode->extends->getResolvedName();
$className = (string) $classNode->extends->getResolvedName();
} else {
$className = (string)$classNode->getNamespacedName();
$className = (string) $classNode->getNamespacedName();
}
} elseif ($scoped->scopeResolutionQualifier instanceof Node\QualifiedName) {
$className = $scoped->scopeResolutionQualifier->getResolvedName();
@ -507,9 +514,9 @@ class DefinitionResolver
if (empty($memberName)) {
return null;
}
$name = (string)$className . '::$' . $memberName;
$name = (string) $className . '::$' . $memberName;
} else {
$name = (string)$className . '::' . $scoped->memberName->getText($scoped->getFileContents());
$name = (string) $className . '::' . $scoped->memberName->getText($scoped->getFileContents());
}
if ($scoped->parent instanceof Node\Expression\CallExpression) {
$name .= '()';
@ -530,7 +537,7 @@ class DefinitionResolver
if ($classNode === null) {
return null;
}
return (string)$classNode->getNamespacedName();
return (string) $classNode->getNamespacedName();
}
/**
@ -561,7 +568,7 @@ class DefinitionResolver
if ($var instanceof Node\UseVariableName) {
$n = $var->getFirstAncestor(Node\Expression\AnonymousFunctionCreationExpression::class)->parent;
$name = $var->getName();
} else if ($var instanceof Node\Expression\Variable || $var instanceof Node\Parameter) {
} elseif ($var instanceof Node\Expression\Variable || $var instanceof Node\Parameter) {
$name = $var->getName();
} else {
throw new \InvalidArgumentException('$var must be Variable, Param or ClosureUse, not ' . get_class($var));
@ -587,9 +594,11 @@ class DefinitionResolver
}
}
// If it is a closure, also check use statements
if ($n instanceof Node\Expression\AnonymousFunctionCreationExpression &&
if (
$n instanceof Node\Expression\AnonymousFunctionCreationExpression &&
$n->anonymousFunctionUseClause !== null &&
$n->anonymousFunctionUseClause->useVariableNameList !== null) {
$n->anonymousFunctionUseClause->useVariableNameList !== null
) {
foreach ($n->anonymousFunctionUseClause->useVariableNameList->getElements() as $use) {
if ($use->getName() === $name) {
return $use;
@ -601,8 +610,7 @@ class DefinitionResolver
// Check each previous sibling node and their descendents for a variable assignment to that variable
// Each previous sibling could contain a declaration of the variable
while (($prevSibling = $n->getPreviousSibling()) !== null && $n = $prevSibling) {
while (($prevSibling = $n->getPreviousSibling()) !== null && ($n = $prevSibling)) {
// Check the sibling itself
if (self::isVariableDeclaration($n, $name)) {
return $n;
@ -615,7 +623,7 @@ class DefinitionResolver
}
}
}
} while (isset($n) && $n = $n->parent);
} while (isset($n) && ($n = $n->parent));
// Return null if nothing was found
return null;
}
@ -631,16 +639,18 @@ class DefinitionResolver
{
if (
// TODO - clean this up
($n instanceof Node\Expression\AssignmentExpression && $n->operator->kind === PhpParser\TokenKind::EqualsToken)
&& $n->leftOperand instanceof Node\Expression\Variable && $n->leftOperand->getName() === $name
$n instanceof Node\Expression\AssignmentExpression &&
$n->operator->kind === PhpParser\TokenKind::EqualsToken &&
$n->leftOperand instanceof Node\Expression\Variable &&
$n->leftOperand->getName() === $name
) {
return true;
}
if (
($n instanceof Node\ForeachValue || $n instanceof Node\ForeachKey)
&& $n->expression instanceof Node\Expression\Variable
&& $n->expression->getName() === $name
($n instanceof Node\ForeachValue || $n instanceof Node\ForeachKey) &&
$n->expression instanceof Node\Expression\Variable &&
$n->expression->getName() === $name
) {
return true;
}
@ -666,7 +676,7 @@ class DefinitionResolver
if ($expr == null || $expr instanceof PhpParser\MissingToken || $expr instanceof PhpParser\SkippedToken) {
// TODO some members are null or Missing/SkippedToken
// How do we handle this more generally?
return new Types\Mixed_;
return new Types\Mixed_();
}
// VARIABLE
@ -691,15 +701,17 @@ class DefinitionResolver
// FUNCTION CALL
// Function calls are resolved to type corresponding to their FQN
if ($expr instanceof Node\Expression\CallExpression &&
if (
$expr instanceof Node\Expression\CallExpression &&
!(
$expr->callableExpression instanceof Node\Expression\ScopedPropertyAccessExpression ||
$expr->callableExpression instanceof Node\Expression\MemberAccessExpression)
$expr->callableExpression instanceof Node\Expression\MemberAccessExpression
)
) {
// Find the function definition
if ($expr->callableExpression instanceof Node\Expression) {
// Cannot get type for dynamic function call
return new Types\Mixed_;
return new Types\Mixed_();
}
if ($expr->callableExpression instanceof Node\QualifiedName) {
@ -717,18 +729,18 @@ class DefinitionResolver
if ($expr instanceof Node\ReservedWord) {
$token = $expr->children->kind;
if ($token === PhpParser\TokenKind::TrueReservedWord || $token === PhpParser\TokenKind::FalseReservedWord) {
return new Types\Boolean;
return new Types\Boolean();
}
if ($token === PhpParser\TokenKind::NullReservedWord) {
return new Types\Null_;
return new Types\Null_();
}
}
// CONSTANT FETCH
// Resolve constants by retrieving corresponding definition type from FQN
if (ParserHelpers\isConstantFetch($expr)) {
$fqn = (string)$expr->getNamespacedName();
$fqn = (string) $expr->getNamespacedName();
$def = $this->index->getDefinition($fqn, true);
if ($def !== null) {
return $def->type;
@ -738,9 +750,10 @@ class DefinitionResolver
// MEMBER CALL EXPRESSION/SCOPED PROPERTY CALL EXPRESSION
// The type of the member/scoped property call expression is the type of the method, so resolve the
// type of the callable expression.
if ($expr instanceof Node\Expression\CallExpression && (
$expr->callableExpression instanceof Node\Expression\MemberAccessExpression ||
$expr->callableExpression instanceof Node\Expression\ScopedPropertyAccessExpression)
if (
$expr instanceof Node\Expression\CallExpression &&
($expr->callableExpression instanceof Node\Expression\MemberAccessExpression ||
$expr->callableExpression instanceof Node\Expression\ScopedPropertyAccessExpression)
) {
return $this->resolveExpressionNodeToType($expr->callableExpression);
}
@ -748,25 +761,25 @@ class DefinitionResolver
// MEMBER ACCESS EXPRESSION
if ($expr instanceof Node\Expression\MemberAccessExpression) {
if ($expr->memberName instanceof Node\Expression) {
return new Types\Mixed_;
return new Types\Mixed_();
}
$var = $expr->dereferencableExpression;
// Resolve object
// Resolve object
$objType = $this->resolveExpressionNodeToType($var);
if (!($objType instanceof Types\Compound)) {
$objType = new Types\Compound([$objType]);
}
for ($i = 0; $t = $objType->get($i); $i++) {
for ($i = 0; ($t = $objType->get($i)); $i++) {
if ($t instanceof Types\This) {
$classFqn = self::getContainingClassFqn($expr);
if ($classFqn === null) {
return new Types\Mixed_;
return new Types\Mixed_();
}
} else if (!($t instanceof Types\Object_) || $t->getFqsen() === null) {
return new Types\Mixed_;
} elseif (!($t instanceof Types\Object_) || $t->getFqsen() === null) {
return new Types\Mixed_();
} else {
$classFqn = substr((string)$t->getFqsen(), 1);
$classFqn = substr((string) $t->getFqsen(), 1);
}
$add = '->' . $expr->memberName->getText($expr->getFileContents());
if ($expr->parent instanceof Node\Expression\CallExpression) {
@ -791,9 +804,9 @@ class DefinitionResolver
if ($expr instanceof Node\Expression\ScopedPropertyAccessExpression) {
$classType = $this->resolveClassNameToType($expr->scopeResolutionQualifier);
if (!($classType instanceof Types\Object_) || $classType->getFqsen() === null) {
return new Types\Mixed_;
return new Types\Mixed_();
}
$fqn = substr((string)$classType->getFqsen(), 1) . '::';
$fqn = substr((string) $classType->getFqsen(), 1) . '::';
// TODO is there a cleaner way to do this?
$fqn .= $expr->memberName->getText() ?? $expr->memberName->getText($expr->getFileContents());
@ -803,7 +816,7 @@ class DefinitionResolver
$def = $this->index->getDefinition($fqn);
if ($def === null) {
return new Types\Mixed_;
return new Types\Mixed_();
}
return $def->type;
}
@ -847,7 +860,10 @@ class DefinitionResolver
// NULL COALLESCE
// $rightOperand ?? $leftOperand => resolves to type of $rightOperand or $leftOperand
if ($expr instanceof Node\Expression\BinaryExpression && $expr->operator->kind === PhpParser\TokenKind::QuestionQuestionToken) {
if (
$expr instanceof Node\Expression\BinaryExpression &&
$expr->operator->kind === PhpParser\TokenKind::QuestionQuestionToken
) {
// ?? operator
return new Types\Compound([
$this->resolveExpressionNodeToType($expr->leftOperand),
@ -862,14 +878,15 @@ class DefinitionResolver
// isset($var)
// >, >=, <, <=, &&, ||, AND, OR, XOR, ==, ===, !=, !==
if (
ParserHelpers\isBooleanExpression($expr)
|| ($expr instanceof Node\Expression\CastExpression && $expr->castType->kind === PhpParser\TokenKind::BoolCastToken)
|| ($expr instanceof Node\Expression\UnaryOpExpression && $expr->operator->kind === PhpParser\TokenKind::ExclamationToken)
|| $expr instanceof Node\Expression\EmptyIntrinsicExpression
|| $expr instanceof Node\Expression\IssetIntrinsicExpression
ParserHelpers\isBooleanExpression($expr) ||
($expr instanceof Node\Expression\CastExpression &&
$expr->castType->kind === PhpParser\TokenKind::BoolCastToken) ||
($expr instanceof Node\Expression\UnaryOpExpression &&
$expr->operator->kind === PhpParser\TokenKind::ExclamationToken) ||
$expr instanceof Node\Expression\EmptyIntrinsicExpression ||
$expr instanceof Node\Expression\IssetIntrinsicExpression
) {
return new Types\Boolean;
return new Types\Boolean();
}
// STRING EXPRESSIONS: resolve to Types\String
@ -880,11 +897,13 @@ class DefinitionResolver
// TODO: Magic constants (__CLASS__, __DIR__, __FUNCTION__, __METHOD__, __NAMESPACE__, __TRAIT__, __FILE__)
if (
($expr instanceof Node\Expression\BinaryExpression &&
($expr->operator->kind === PhpParser\TokenKind::DotToken || $expr->operator->kind === PhpParser\TokenKind::DotEqualsToken)) ||
($expr->operator->kind === PhpParser\TokenKind::DotToken ||
$expr->operator->kind === PhpParser\TokenKind::DotEqualsToken)) ||
$expr instanceof Node\StringLiteral ||
($expr instanceof Node\Expression\CastExpression && $expr->castType->kind === PhpParser\TokenKind::StringCastToken)
($expr instanceof Node\Expression\CastExpression &&
$expr->castType->kind === PhpParser\TokenKind::StringCastToken)
) {
return new Types\String_;
return new Types\String_();
}
// BINARY EXPRESSIONS:
@ -894,32 +913,30 @@ class DefinitionResolver
// Resolve to Types\Float
// [assignment] /=
if (
// Assignment expressions (TODO: consider making this a type of AssignmentExpression rather than kind of BinaryExpression)
$expr instanceof Node\Expression\BinaryExpression &&
($operator = $expr->operator->kind)
&& ($operator === PhpParser\TokenKind::PlusToken ||
($operator = $expr->operator->kind) &&
($operator === PhpParser\TokenKind::PlusToken ||
$operator === PhpParser\TokenKind::AsteriskAsteriskToken ||
$operator === PhpParser\TokenKind::AsteriskToken ||
$operator === PhpParser\TokenKind::MinusToken ||
// Assignment expressions (TODO: consider making this a type of AssignmentExpression rather than kind of BinaryExpression)
$operator === PhpParser\TokenKind::AsteriskEqualsToken||
$operator === PhpParser\TokenKind::AsteriskEqualsToken ||
$operator === PhpParser\TokenKind::AsteriskAsteriskEqualsToken ||
$operator === PhpParser\TokenKind::MinusEqualsToken ||
$operator === PhpParser\TokenKind::PlusEqualsToken
)
$operator === PhpParser\TokenKind::PlusEqualsToken)
) {
if (
$this->resolveExpressionNodeToType($expr->leftOperand) instanceof Types\Integer
&& $this->resolveExpressionNodeToType($expr->rightOperand) instanceof Types\Integer
$this->resolveExpressionNodeToType($expr->leftOperand) instanceof Types\Integer &&
$this->resolveExpressionNodeToType($expr->rightOperand) instanceof Types\Integer
) {
return new Types\Integer;
return new Types\Integer();
}
return new Types\Float_;
} else if (
return new Types\Float_();
} elseif (
$expr instanceof Node\Expression\BinaryExpression &&
$expr->operator->kind === PhpParser\TokenKind::SlashEqualsToken
) {
return new Types\Float_;
return new Types\Float_();
}
// INTEGER EXPRESSIONS: resolve to Types\Integer
@ -928,16 +945,16 @@ class DefinitionResolver
// TODO: Magic constants (__LINE__)
if (
// TODO: consider different Node types of float/int, also better property name (not "children")
($expr instanceof Node\NumericLiteral && $expr->children->kind === PhpParser\TokenKind::IntegerLiteralToken) ||
$expr instanceof Node\Expression\BinaryExpression && (
($operator = $expr->operator->kind)
&& ($operator === PhpParser\TokenKind::LessThanEqualsGreaterThanToken ||
$operator === PhpParser\TokenKind::AmpersandToken ||
$operator === PhpParser\TokenKind::CaretToken ||
$operator === PhpParser\TokenKind::BarToken)
)
($expr instanceof Node\NumericLiteral &&
$expr->children->kind === PhpParser\TokenKind::IntegerLiteralToken) ||
($expr instanceof Node\Expression\BinaryExpression &&
(($operator = $expr->operator->kind) &&
($operator === PhpParser\TokenKind::LessThanEqualsGreaterThanToken ||
$operator === PhpParser\TokenKind::AmpersandToken ||
$operator === PhpParser\TokenKind::CaretToken ||
$operator === PhpParser\TokenKind::BarToken)))
) {
return new Types\Integer;
return new Types\Integer();
}
// FLOAT EXPRESSIONS: resolve to Types\Float
@ -945,11 +962,14 @@ class DefinitionResolver
// [operator] /
// [cast] (double)
if (
$expr instanceof Node\NumericLiteral && $expr->children->kind === PhpParser\TokenKind::FloatingLiteralToken ||
($expr instanceof Node\Expression\CastExpression && $expr->castType->kind === PhpParser\TokenKind::DoubleCastToken) ||
($expr instanceof Node\Expression\BinaryExpression && $expr->operator->kind === PhpParser\TokenKind::SlashToken)
($expr instanceof Node\NumericLiteral &&
$expr->children->kind === PhpParser\TokenKind::FloatingLiteralToken) ||
($expr instanceof Node\Expression\CastExpression &&
$expr->castType->kind === PhpParser\TokenKind::DoubleCastToken) ||
($expr instanceof Node\Expression\BinaryExpression &&
$expr->operator->kind === PhpParser\TokenKind::SlashToken)
) {
return new Types\Float_;
return new Types\Float_();
}
// ARRAY CREATION EXPRESSION:
@ -962,21 +982,23 @@ class DefinitionResolver
if ($expr->arrayElements !== null) {
foreach ($expr->arrayElements->getElements() as $item) {
$valueTypes[] = $this->resolveExpressionNodeToType($item->elementValue);
$keyTypes[] = $item->elementKey ? $this->resolveExpressionNodeToType($item->elementKey) : new Types\Integer;
$keyTypes[] = $item->elementKey
? $this->resolveExpressionNodeToType($item->elementKey)
: new Types\Integer();
}
}
$valueTypes = array_unique($valueTypes);
$keyTypes = array_unique($keyTypes);
if (empty($valueTypes)) {
$valueType = null;
} else if (count($valueTypes) === 1) {
} elseif (count($valueTypes) === 1) {
$valueType = $valueTypes[0];
} else {
$valueType = new Types\Compound($valueTypes);
}
if (empty($keyTypes)) {
$keyType = null;
} else if (count($keyTypes) === 1) {
} elseif (count($keyTypes) === 1) {
$keyType = $keyTypes[0];
} else {
$keyType = new Types\Compound($keyTypes);
@ -990,7 +1012,7 @@ class DefinitionResolver
if ($expr instanceof Node\Expression\SubscriptExpression) {
$varType = $this->resolveExpressionNodeToType($expr->postfixExpression);
if (!($varType instanceof Types\Array_)) {
return new Types\Mixed_;
return new Types\Mixed_();
}
return $varType->getValueType();
}
@ -999,17 +1021,16 @@ class DefinitionResolver
// include, require, include_once, require_once
if ($expr instanceof Node\Expression\ScriptInclusionExpression) {
// TODO: resolve path to PhpDocument and find return statement
return new Types\Mixed_;
return new Types\Mixed_();
}
if ($expr instanceof Node\QualifiedName) {
return $this->resolveClassNameToType($expr);
}
return new Types\Mixed_;
return new Types\Mixed_();
}
/**
* Takes any class name node (from a static method call, or new node) and returns a Type object
* Resolves keywords like self, static and parent
@ -1020,32 +1041,32 @@ class DefinitionResolver
public function resolveClassNameToType($class): Type
{
if ($class instanceof Node\Expression) {
return new Types\Mixed_;
return new Types\Mixed_();
}
if ($class instanceof PhpParser\Token && $class->kind === PhpParser\TokenKind::ClassKeyword) {
// Anonymous class
return new Types\Object_;
return new Types\Object_();
}
if ($class instanceof PhpParser\Token && $class->kind === PhpParser\TokenKind::StaticKeyword) {
// `new static`
return new Types\Static_;
return new Types\Static_();
}
$className = (string)$class->getResolvedName();
$className = (string) $class->getResolvedName();
if ($className === 'self' || $className === 'parent') {
$classNode = $class->getFirstAncestor(Node\Statement\ClassDeclaration::class);
if ($className === 'parent') {
if ($classNode === null || $classNode->classBaseClause === null) {
return new Types\Object_;
return new Types\Object_();
}
// parent is resolved to the parent class
$classFqn = (string)$classNode->classBaseClause->baseClass->getResolvedName();
$classFqn = (string) $classNode->classBaseClause->baseClass->getResolvedName();
} else {
if ($classNode === null) {
return new Types\Self_;
return new Types\Self_();
}
// self is resolved to the containing class
$classFqn = (string)$classNode->getNamespacedName();
$classFqn = (string) $classNode->getNamespacedName();
}
return new Types\Object_(new Fqsen('\\' . $classFqn));
}
@ -1102,7 +1123,7 @@ class DefinitionResolver
// Resolve a string like "bool" to a type object
$type = $this->typeResolver->resolve($node->typeDeclaration->getText($node->getFileContents()));
} else {
$type = new Types\Object_(new Fqsen('\\' . (string)$node->typeDeclaration->getResolvedName()));
$type = new Types\Object_(new Fqsen('\\' . (string) $node->typeDeclaration->getResolvedName()));
}
}
// function foo($a = 3)
@ -1114,7 +1135,7 @@ class DefinitionResolver
}
$type = $defaultType;
}
return $type ?? new Types\Mixed_;
return $type ?? new Types\Mixed_();
}
// FUNCTIONS AND METHODS
@ -1126,9 +1147,9 @@ class DefinitionResolver
// Functions/methods
$docBlock = $this->getDocBlock($node);
if (
$docBlock !== null
&& !empty($returnTags = $docBlock->getTagsByName('return'))
&& $returnTags[0]->getType() !== null
$docBlock !== null &&
!empty(($returnTags = $docBlock->getTagsByName('return'))) &&
$returnTags[0]->getType() !== null
) {
// Use @return tag
$returnType = $returnTags[0]->getType();
@ -1151,10 +1172,10 @@ class DefinitionResolver
return $selfType;
}
}
return new Types\Object_(new Fqsen('\\' . (string)$node->returnType->getResolvedName()));
return new Types\Object_(new Fqsen('\\' . (string) $node->returnType->getResolvedName()));
}
// Unknown return type
return new Types\Mixed_;
return new Types\Mixed_();
}
// FOREACH KEY/VARIABLE
@ -1168,8 +1189,9 @@ class DefinitionResolver
}
// FOREACH VALUE/VARIABLE
if ($node instanceof Node\ForeachValue
|| ($node instanceof Node\Expression\Variable && $node->parent instanceof Node\ForeachValue)
if (
$node instanceof Node\ForeachValue ||
($node instanceof Node\Expression\Variable && $node->parent instanceof Node\ForeachValue)
) {
$foreach = $node->getFirstAncestor(Node\Statement\ForeachStatement::class);
$collectionType = $this->resolveExpressionNodeToType($foreach->forEachCollectionName);
@ -1184,17 +1206,17 @@ class DefinitionResolver
if (
($declarationNode =
ParserHelpers\tryGetPropertyDeclaration($node) ??
ParserHelpers\tryGetConstOrClassConstDeclaration($node)
) !== null ||
($node = $node->parent) instanceof Node\Expression\AssignmentExpression) {
ParserHelpers\tryGetConstOrClassConstDeclaration($node)) !== null ||
($node = $node->parent) instanceof Node\Expression\AssignmentExpression
) {
$declarationNode = $declarationNode ?? $node;
// Property, constant or variable
// Use @var tag
if (
($docBlock = $this->getDocBlock($declarationNode))
&& !empty($varTags = $docBlock->getTagsByName('var'))
&& ($type = $varTags[0]->getType())
($docBlock = $this->getDocBlock($declarationNode)) &&
!empty(($varTags = $docBlock->getTagsByName('var'))) &&
($type = $varTags[0]->getType())
) {
return $type;
}
@ -1205,15 +1227,15 @@ class DefinitionResolver
if (isset($node->parent->rightOperand)) {
return $this->resolveExpressionNodeToType($node->parent->rightOperand);
}
} else if ($node instanceof Node\ConstElement) {
} elseif ($node instanceof Node\ConstElement) {
return $this->resolveExpressionNodeToType($node->assignment);
} else if ($node instanceof Node\Expression\AssignmentExpression) {
} elseif ($node instanceof Node\Expression\AssignmentExpression) {
return $this->resolveExpressionNodeToType($node->rightOperand);
}
// TODO: read @property tags of class
// TODO: Try to infer the type from default value / constant value
// Unknown
return new Types\Mixed_;
return new Types\Mixed_();
}
// The node does not have a type
@ -1236,10 +1258,8 @@ class DefinitionResolver
// class C { } A\B\C
// interface C { } A\B\C
// trait C { } A\B\C
if (
$node instanceof PhpParser\ClassLike
) {
$className = (string)$node->getNamespacedName();
if ($node instanceof PhpParser\ClassLike) {
$className = (string) $node->getNamespacedName();
// An (invalid) class declaration without a name will have an empty string as name,
// but should not define an FQN
if ($className === '') {
@ -1260,7 +1280,7 @@ class DefinitionResolver
// function a(); A\B\a();
if ($node instanceof Node\Statement\FunctionDeclaration) {
// Function: use functionName() as the name
$name = (string)$node->getNamespacedName();
$name = (string) $node->getNamespacedName();
return $name === "" ? null : $name . '()';
}
@ -1281,9 +1301,9 @@ class DefinitionResolver
return null;
}
if ($node->isStatic()) {
return (string)$class->getNamespacedName() . '::' . $node->getName() . '()';
return (string) $class->getNamespacedName() . '::' . $node->getName() . '()';
} else {
return (string)$class->getNamespacedName() . '->' . $node->getName() . '()';
return (string) $class->getNamespacedName() . '->' . $node->getName() . '()';
}
}
@ -1295,20 +1315,20 @@ class DefinitionResolver
// }
if (
($propertyDeclaration = ParserHelpers\tryGetPropertyDeclaration($node)) !== null &&
($classDeclaration =
$node->getFirstAncestor(
Node\Expression\ObjectCreationExpression::class,
PhpParser\ClassLike::class
)
) !== null && isset($classDeclaration->name)) {
($classDeclaration = $node->getFirstAncestor(
Node\Expression\ObjectCreationExpression::class,
PhpParser\ClassLike::class
)) !== null &&
isset($classDeclaration->name)
) {
$name = $node->getName();
if ($propertyDeclaration->isStatic()) {
// Static Property: use ClassName::$propertyName as name
return (string)$classDeclaration->getNamespacedName() . '::$' . $name;
return (string) $classDeclaration->getNamespacedName() . '::$' . $name;
}
// Instance Property: use ClassName->propertyName as name
return (string)$classDeclaration->getNamespacedName() . '->' . $name;
return (string) $classDeclaration->getNamespacedName() . '->' . $name;
}
// INPUT OUTPUT
@ -1320,7 +1340,7 @@ class DefinitionResolver
if (($constDeclaration = ParserHelpers\tryGetConstOrClassConstDeclaration($node)) !== null) {
if ($constDeclaration instanceof Node\Statement\ConstDeclaration) {
// Basic constant: use CONSTANT_NAME as name
return (string)$node->getNamespacedName();
return (string) $node->getNamespacedName();
}
// Class constant: use ClassName::CONSTANT_NAME as name
@ -1332,7 +1352,7 @@ class DefinitionResolver
if (!isset($classDeclaration->name)) {
return null;
}
return (string)$classDeclaration->getNamespacedName() . '::' . $node->getName();
return (string) $classDeclaration->getNamespacedName() . '::' . $node->getName();
}
if (ParserHelpers\isConstDefineExpression($node)) {

View File

@ -17,12 +17,12 @@ class CompletionItemFactory
*/
public static function fromDefinition(Definition $def)
{
$item = new CompletionItem;
$item = new CompletionItem();
$item->label = $def->symbolInformation->name;
$item->kind = CompletionItemKind::fromSymbolKind($def->symbolInformation->kind);
if ($def->type) {
$item->detail = (string)$def->type;
} else if ($def->symbolInformation->containerName) {
$item->detail = (string) $def->type;
} elseif ($def->symbolInformation->containerName) {
$item->detail = $def->symbolInformation->containerName;
}
if ($def->documentation) {

View File

@ -24,9 +24,12 @@ class LocationFactory
$node->getFileContents()
);
return new Location($node->getUri(), new Range(
new Position($range->start->line, $range->start->character),
new Position($range->end->line, $range->end->character)
));
return new Location(
$node->getUri(),
new Range(
new Position($range->start->line, $range->start->character),
new Position($range->end->line, $range->end->character)
)
);
}
}

View File

@ -23,37 +23,38 @@ class SymbolInformationFactory
$symbol = new SymbolInformation();
if ($node instanceof Node\Statement\ClassDeclaration) {
$symbol->kind = SymbolKind::CLASS_;
} else if ($node instanceof Node\Statement\TraitDeclaration) {
} elseif ($node instanceof Node\Statement\TraitDeclaration) {
$symbol->kind = SymbolKind::CLASS_;
} else if (\LanguageServer\ParserHelpers\isConstDefineExpression($node)) {
} elseif (\LanguageServer\ParserHelpers\isConstDefineExpression($node)) {
// constants with define() like
// define('TEST_DEFINE_CONSTANT', false);
$symbol->kind = SymbolKind::CONSTANT;
$symbol->name = $node->argumentExpressionList->children[0]->expression->getStringContentsText();
} else if ($node instanceof Node\Statement\InterfaceDeclaration) {
} elseif ($node instanceof Node\Statement\InterfaceDeclaration) {
$symbol->kind = SymbolKind::INTERFACE;
} else if ($node instanceof Node\Statement\NamespaceDefinition) {
} elseif ($node instanceof Node\Statement\NamespaceDefinition) {
$symbol->kind = SymbolKind::NAMESPACE;
} else if ($node instanceof Node\Statement\FunctionDeclaration) {
} elseif ($node instanceof Node\Statement\FunctionDeclaration) {
$symbol->kind = SymbolKind::FUNCTION;
} else if ($node instanceof Node\MethodDeclaration) {
} elseif ($node instanceof Node\MethodDeclaration) {
$nameText = $node->getName();
if ($nameText === '__construct' || $nameText === '__destruct') {
$symbol->kind = SymbolKind::CONSTRUCTOR;
} else {
$symbol->kind = SymbolKind::METHOD;
}
} else if ($node instanceof Node\Expression\Variable && $node->getFirstAncestor(Node\PropertyDeclaration::class) !== null) {
} elseif (
$node instanceof Node\Expression\Variable &&
$node->getFirstAncestor(Node\PropertyDeclaration::class) !== null
) {
$symbol->kind = SymbolKind::PROPERTY;
} else if ($node instanceof Node\ConstElement) {
} elseif ($node instanceof Node\ConstElement) {
$symbol->kind = SymbolKind::CONSTANT;
} else if (
(
($node instanceof Node\Expression\AssignmentExpression)
&& $node->leftOperand instanceof Node\Expression\Variable
)
|| $node instanceof Node\UseVariableName
|| $node instanceof Node\Parameter
} elseif (
($node instanceof Node\Expression\AssignmentExpression &&
$node->leftOperand instanceof Node\Expression\Variable) ||
$node instanceof Node\UseVariableName ||
$node instanceof Node\Parameter
) {
$symbol->kind = SymbolKind::VARIABLE;
} else {
@ -66,17 +67,17 @@ class SymbolInformationFactory
} elseif ($node->leftOperand instanceof PhpParser\Token) {
$symbol->name = trim($node->leftOperand->getText($node->getFileContents()), "$");
}
} else if ($node instanceof Node\UseVariableName) {
} elseif ($node instanceof Node\UseVariableName) {
$symbol->name = $node->getName();
} else if (isset($node->name)) {
} elseif (isset($node->name)) {
if ($node->name instanceof Node\QualifiedName) {
$symbol->name = (string)ResolvedName::buildName($node->name->nameParts, $node->getFileContents());
$symbol->name = (string) ResolvedName::buildName($node->name->nameParts, $node->getFileContents());
} else {
$symbol->name = ltrim((string)$node->name->getText($node->getFileContents()), "$");
$symbol->name = ltrim((string) $node->name->getText($node->getFileContents()), "$");
}
} else if (isset($node->variableName)) {
} elseif (isset($node->variableName)) {
$symbol->name = $node->variableName->getText($node);
} else if (!isset($symbol->name)) {
} elseif (!isset($symbol->name)) {
return null;
}

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\FilesFinder;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\FilesFinder;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\FilesFinder;

View File

@ -16,11 +16,11 @@ function getFqnsFromType($type): array
if ($type instanceof Types\Object_) {
$fqsen = $type->getFqsen();
if ($fqsen !== null) {
$fqns[] = substr((string)$fqsen, 1);
$fqns[] = substr((string) $fqsen, 1);
}
}
if ($type instanceof Types\Compound) {
for ($i = 0; $t = $type->get($i); $i++) {
for ($i = 0; ($t = $type->get($i)); $i++) {
foreach (getFqnsFromType($t) as $fqn) {
$fqns[] = $fqn;
}
@ -42,7 +42,8 @@ function getFqnsFromType($type): array
*/
function nameGetParent(string $name): string
{
if ($name === '') { // Special-case handling for the root namespace.
if ($name === '') {
// Special-case handling for the root namespace.
return '';
}
$parts = explode('\\', $name);
@ -113,6 +114,5 @@ function nameWithoutFirstPart(string $name): string
*/
function nameStartsWith(string $name, string $prefix): bool
{
return strlen($name) >= strlen($prefix)
&& strncmp($name, $prefix, strlen($prefix)) === 0;
return strlen($name) >= strlen($prefix) && strncmp($name, $prefix, strlen($prefix)) === 0;
}

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Index;
@ -134,7 +134,7 @@ abstract class AbstractAggregateIndex implements ReadableIndex
public function getDefinition(string $fqn, bool $globalFallback = false)
{
foreach ($this->getIndexes() as $index) {
if ($def = $index->getDefinition($fqn, $globalFallback)) {
if (($def = $index->getDefinition($fqn, $globalFallback))) {
return $def;
}
}

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Index;
@ -27,7 +27,7 @@ class DependenciesIndex extends AbstractAggregateIndex
public function getDependencyIndex(string $packageName): Index
{
if (!isset($this->indexes[$packageName])) {
$index = new Index;
$index = new Index();
$this->indexes[$packageName] = $index;
$this->registerIndex($index);
}

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Index;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Index;
@ -131,9 +131,9 @@ class Index implements ReadableIndex, \Serializable
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[''];
}
}
}
@ -299,9 +299,9 @@ class Index implements ReadableIndex, \Serializable
{
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);
}
}
}

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Index;
@ -24,8 +24,11 @@ class ProjectIndex extends AbstractAggregateIndex
*/
private $sourceIndex;
public function __construct(Index $sourceIndex, DependenciesIndex $dependenciesIndex, \stdClass $composerJson = null)
{
public function __construct(
Index $sourceIndex,
DependenciesIndex $dependenciesIndex,
\stdClass $composerJson = null
) {
$this->sourceIndex = $sourceIndex;
$this->dependenciesIndex = $dependenciesIndex;
$this->composerJson = $composerJson;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Index;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Index;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer;
@ -103,9 +103,8 @@ class Indexer
public function index(): Promise
{
return coroutine(function () {
$pattern = Path::makeAbsolute('**/*.php', $this->rootPath);
$uris = yield $this->filesFinder->find($pattern);
$uris = (yield $this->filesFinder->find($pattern));
$count = count($uris);
$startTime = microtime(true);
@ -132,7 +131,10 @@ class Indexer
// Index source
// Definitions and static references
$this->client->window->logMessage(MessageType::INFO, 'Indexing project for 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
@ -147,7 +149,10 @@ class Indexer
$packageKey = null;
$cacheKey = null;
$index = null;
foreach (array_merge($this->composerLock->packages, (array)$this->composerLock->{'packages-dev'}) as $package) {
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');
@ -155,7 +160,7 @@ class Indexer
$packageKey = $packageName . ':' . $packageVersion;
$cacheKey = self::CACHE_VERSION . ':' . $packageKey;
// Check cache
$index = yield $this->cache->get($cacheKey);
$index = (yield $this->cache->get($cacheKey));
break;
}
}
@ -168,12 +173,18 @@ class Indexer
$index = $this->dependenciesIndex->getDependencyIndex($packageName);
// Index definitions and static references
$this->client->window->logMessage(MessageType::INFO, 'Indexing ' . ($packageKey ?? $packageName) . ' for 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');
$this->client->window->logMessage(
MessageType::INFO,
'Indexing ' . ($packageKey ?? $packageName) . ' for dynamic references'
);
yield $this->indexFiles($files);
$index->setComplete();
@ -182,13 +193,16 @@ class Indexer
$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");
$this->client->window->logMessage(
MessageType::WARNING,
"Could not compute cache key for $packageName"
);
}
}
}
$duration = (int)(microtime(true) - $startTime);
$mem = (int)(memory_get_usage(true) / (1024 * 1024));
$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."
@ -213,7 +227,7 @@ class Indexer
yield timeout();
$this->client->window->logMessage(MessageType::LOG, "Parsing $uri");
try {
$document = yield $this->documentLoader->load($uri);
$document = (yield $this->documentLoader->load($uri));
if (!isVendored($document, $this->composerJson)) {
$this->client->textDocument->publishDiagnostics($uri, $document->getDiagnostics());
}
@ -223,7 +237,7 @@ class Indexer
"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);
$this->client->window->logMessage(MessageType::ERROR, "Error parsing $uri: " . (string) $e);
}
}
});

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer;
@ -38,7 +38,7 @@ class LanguageClient
public function __construct(ProtocolReader $reader, ProtocolWriter $writer)
{
$handler = new ClientHandler($reader, $writer);
$mapper = new JsonMapper;
$mapper = new JsonMapper();
$this->textDocument = new Client\TextDocument($handler, $mapper);
$this->window = new Client\Window($handler);

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer;
@ -119,30 +119,30 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher
$this->exit();
});
$this->protocolReader->on('message', function (Message $msg) {
// Ignore responses, this is the handler for requests and notifications
// Invoke the method handler to get a result
// If a ResponseError is thrown, send it back in the Response
// If an unexpected error occurred, send back an INTERNAL_ERROR error response
// Only send a Response for a Request
// Notifications do not send Responses
coroutine(function () use ($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 = yield $this->dispatch($msg->body);
$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 occurred, send back an INTERNAL_ERROR error response
$error = new AdvancedJsonRpc\Error(
(string)$e,
(string) $e,
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);
@ -165,27 +165,30 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher
* @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.
* @return Promise <InitializeResult>
*/
public function initialize(ClientCapabilities $capabilities, string $rootPath = null, int $processId = null, string $rootUri = null): 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) {
if ($capabilities->xfilesProvider) {
$this->filesFinder = new ClientFilesFinder($this->client);
} else {
$this->filesFinder = new FileSystemFilesFinder;
$this->filesFinder = new FileSystemFilesFinder();
}
if ($capabilities->xcontentProvider) {
$this->contentRetriever = new ClientContentRetriever($this->client);
} else {
$this->contentRetriever = new FileSystemContentRetriever;
$this->contentRetriever = new FileSystemContentRetriever();
}
$dependenciesIndex = new DependenciesIndex;
$sourceIndex = new Index;
$dependenciesIndex = new DependenciesIndex();
$sourceIndex = new Index();
$this->projectIndex = new ProjectIndex($sourceIndex, $dependenciesIndex, $this->composerJson);
$stubsIndex = StubsIndex::read();
$this->globalIndex = new GlobalIndex($stubsIndex, $this->projectIndex);
@ -204,25 +207,33 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher
// Find composer.json
if ($this->composerJson === null) {
$composerJsonFiles = yield $this->filesFinder->find(Path::makeAbsolute('**/composer.json', $rootPath));
$composerJsonFiles = (yield $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 $this->contentRetriever->retrieve($composerJsonFiles[0])
);
}
}
// Find composer.lock
if ($this->composerLock === null) {
$composerLockFiles = yield $this->filesFinder->find(Path::makeAbsolute('**/composer.lock', $rootPath));
$composerLockFiles = (yield $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 $this->contentRetriever->retrieve($composerLockFiles[0])
);
}
}
$cache = $capabilities->xcacheProvider ? new ClientCache($this->client) : new FileSystemCache;
$cache = $capabilities->xcacheProvider ? new ClientCache($this->client) : new FileSystemCache();
// Index in background
$indexer = new Indexer(
@ -239,7 +250,6 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher
$indexer->index()->otherwise('\\LanguageServer\\crash');
}
if ($this->textDocument === null) {
$this->textDocument = new Server\TextDocument(
$this->documentLoader,
@ -276,7 +286,7 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher
// Support "Hover"
$serverCapabilities->hoverProvider = true;
// Support "Completion"
$serverCapabilities->completionProvider = new CompletionOptions;
$serverCapabilities->completionProvider = new CompletionOptions();
$serverCapabilities->completionProvider->resolveProvider = false;
$serverCapabilities->completionProvider->triggerCharacters = ['$', '>'];

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer;
@ -26,7 +26,7 @@ class Message
*/
public static function parse(string $msg): Message
{
$obj = new self;
$obj = new self();
$parts = explode("\r\n", $msg);
$obj->body = MessageBody::parse(array_pop($parts));
foreach ($parts as $line) {
@ -53,7 +53,7 @@ class Message
public function __toString(): string
{
$body = (string)$this->body;
$body = (string) $this->body;
$contentLength = strlen($body);
$this->headers['Content-Length'] = $contentLength;
$headers = '';

View File

@ -6,31 +6,25 @@ namespace LanguageServer\ParserHelpers;
use Microsoft\PhpParser;
use Microsoft\PhpParser\Node;
function isConstantFetch(Node $node) : bool
function isConstantFetch(Node $node): bool
{
$parent = $node->parent;
return
(
$node instanceof Node\QualifiedName &&
(
$parent instanceof Node\Expression ||
return $node instanceof Node\QualifiedName &&
($parent instanceof Node\Expression ||
$parent instanceof Node\DelimitedList\ExpressionList ||
$parent instanceof Node\ArrayElement ||
($parent instanceof Node\Parameter && $node->parent->default === $node) ||
$parent instanceof Node\StatementNode ||
$parent instanceof Node\CaseStatementNode
) &&
$parent instanceof Node\CaseStatementNode) &&
!(
$parent instanceof Node\Expression\MemberAccessExpression ||
$parent instanceof Node\Expression\CallExpression ||
$parent instanceof Node\Expression\ObjectCreationExpression ||
$parent instanceof Node\Expression\ScopedPropertyAccessExpression ||
$parent instanceof PhpParser\FunctionLike ||
(
$parent instanceof Node\Expression\BinaryExpression &&
$parent->operator->kind === PhpParser\TokenKind::InstanceOfKeyword
)
));
($parent instanceof Node\Expression\BinaryExpression &&
$parent->operator->kind === PhpParser\TokenKind::InstanceOfKeyword)
);
}
function getFunctionLikeDeclarationFromParameter(Node\Parameter $node)
@ -38,7 +32,7 @@ function getFunctionLikeDeclarationFromParameter(Node\Parameter $node)
return $node->parent->parent;
}
function isBooleanExpression($expression) : bool
function isBooleanExpression($expression): bool
{
if (!($expression instanceof Node\Expression\BinaryExpression)) {
return false;
@ -65,7 +59,6 @@ function isBooleanExpression($expression) : bool
return false;
}
/**
* Tries to get the parent property declaration given a Node
* @param Node $node
@ -73,7 +66,8 @@ function isBooleanExpression($expression) : bool
*/
function tryGetPropertyDeclaration(Node $node)
{
if ($node instanceof Node\Expression\Variable &&
if (
$node instanceof Node\Expression\Variable &&
(($propertyDeclaration = $node->parent->parent) instanceof Node\PropertyDeclaration ||
($propertyDeclaration = $propertyDeclaration->parent) instanceof Node\PropertyDeclaration)
) {
@ -90,10 +84,10 @@ function tryGetPropertyDeclaration(Node $node)
function tryGetConstOrClassConstDeclaration(Node $node)
{
if (
$node instanceof Node\ConstElement && (
($constDeclaration = $node->parent->parent) instanceof Node\ClassConstDeclaration ||
$constDeclaration instanceof Node\Statement\ConstDeclaration )
) {
$node instanceof Node\ConstElement &&
(($constDeclaration = $node->parent->parent) instanceof Node\ClassConstDeclaration ||
$constDeclaration instanceof Node\Statement\ConstDeclaration)
) {
return $constDeclaration;
}
return null;
@ -107,10 +101,10 @@ function tryGetConstOrClassConstDeclaration(Node $node)
*/
function isConstDefineExpression(Node $node): bool
{
return $node instanceof Node\Expression\CallExpression
&& $node->callableExpression instanceof Node\QualifiedName
&& strtolower($node->callableExpression->getText()) === 'define'
&& isset($node->argumentExpressionList->children[0])
&& $node->argumentExpressionList->children[0]->expression instanceof Node\StringLiteral
&& isset($node->argumentExpressionList->children[2]);
return $node instanceof Node\Expression\CallExpression &&
$node->callableExpression instanceof Node\QualifiedName &&
strtolower($node->callableExpression->getText()) === 'define' &&
isset($node->argumentExpressionList->children[0]) &&
$node->argumentExpressionList->children[0]->expression instanceof Node\StringLiteral &&
isset($node->argumentExpressionList->children[2]);
}

View File

@ -1,12 +1,10 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer;
use LanguageServer\Index\Index;
use LanguageServerProtocol\{
Diagnostic, Position, Range
};
use LanguageServerProtocol\{Diagnostic, Position, Range};
use Microsoft\PhpParser;
use Microsoft\PhpParser\Node;
use phpDocumentor\Reflection\DocBlockFactory;
@ -144,7 +142,13 @@ class PhpDocument
$this->definitions = null;
$this->definitionNodes = null;
$treeAnalyzer = new TreeAnalyzer($this->parser, $content, $this->docBlockFactory, $this->definitionResolver, $this->uri);
$treeAnalyzer = new TreeAnalyzer(
$this->parser,
$content,
$this->docBlockFactory,
$this->definitionResolver,
$this->uri
);
$this->diagnostics = $treeAnalyzer->getDiagnostics();
@ -162,7 +166,7 @@ class PhpDocument
foreach ($this->referenceNodes as $fqn => $nodes) {
// Cast the key to string. If (string)'2' is set as an array index, it will read out as (int)2. We must
// deal with incorrect code, so this is a valid scenario.
$this->index->addReferenceUri((string)$fqn, $this->uri);
$this->index->addReferenceUri((string) $fqn, $this->uri);
}
$this->sourceFileNode = $treeAnalyzer->getSourceFileNode();

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer;
@ -105,9 +105,8 @@ class PhpDocumentLoader
public function load(string $uri): Promise
{
return coroutine(function () use ($uri) {
$limit = 150000;
$content = yield $this->contentRetriever->retrieve($uri);
$content = (yield $this->contentRetriever->retrieve($uri));
$size = strlen($content);
if ($size > $limit) {
throw new ContentTooLargeException($uri, $size, $limit);

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer;
@ -13,5 +13,4 @@ use Sabre\Event\EmitterInterface;
*/
interface ProtocolReader extends EmitterInterface
{
}

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer;
@ -42,9 +42,9 @@ class ProtocolStreamReader extends Emitter implements ProtocolReader
case self::PARSE_HEADERS:
if ($this->buffer === "\r\n") {
$this->parsingMode = self::PARSE_BODY;
$this->contentLength = (int)$this->headers['Content-Length'];
$this->contentLength = (int) $this->headers['Content-Length'];
$this->buffer = '';
} else if (substr($this->buffer, -2) === "\r\n") {
} elseif (substr($this->buffer, -2) === "\r\n") {
$parts = explode(':', $this->buffer);
$this->headers[$parts[0]] = trim($parts[1]);
$this->buffer = '';

View File

@ -1,13 +1,10 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer;
use LanguageServer\Message;
use Sabre\Event\{
Loop,
Promise
};
use Sabre\Event\{Loop, Promise};
class ProtocolStreamWriter implements ProtocolWriter
{
@ -43,7 +40,7 @@ class ProtocolStreamWriter implements ProtocolWriter
$promise = new Promise();
$this->messages[] = [
'message' => (string)$msg,
'message' => (string) $msg,
'promise' => $promise
];
return $promise;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer;

View File

@ -1,10 +1,15 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Server;
use LanguageServer\{
CompletionProvider, SignatureHelpProvider, LanguageClient, PhpDocument, PhpDocumentLoader, DefinitionResolver
CompletionProvider,
SignatureHelpProvider,
LanguageClient,
PhpDocument,
PhpDocumentLoader,
DefinitionResolver
};
use LanguageServer\Index\ReadableIndex;
use LanguageServer\Factory\LocationFactory;
@ -28,9 +33,7 @@ use LanguageServerProtocol\{
use Microsoft\PhpParser\Node;
use Sabre\Event\Promise;
use Sabre\Uri;
use function LanguageServer\{
isVendored, waitForEvent, getPackageName
};
use function LanguageServer\{isVendored, waitForEvent, getPackageName};
use function Sabre\Event\coroutine;
/**
@ -180,7 +183,7 @@ class TextDocument
Position $position
): Promise {
return coroutine(function () use ($textDocument, $position) {
$document = yield $this->documentLoader->getOrLoad($textDocument->uri);
$document = (yield $this->documentLoader->getOrLoad($textDocument->uri));
$node = $document->getNodeAtPosition($position);
if ($node === null) {
return [];
@ -189,10 +192,10 @@ class TextDocument
// 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\Parameter
|| $node instanceof Node\UseVariableName
($node instanceof Node\Expression\Variable &&
!($node->getParent()->getParent() instanceof Node\PropertyDeclaration)) ||
$node instanceof Node\Parameter ||
$node instanceof Node\UseVariableName
) {
if (isset($node->name) && $node->name instanceof Node\Expression) {
return null;
@ -200,14 +203,20 @@ class TextDocument
// Find function/method/closure scope
$n = $node;
$n = $n->getFirstAncestor(Node\Statement\FunctionDeclaration::class, Node\MethodDeclaration::class, Node\Expression\AnonymousFunctionCreationExpression::class, Node\SourceFileNode::class);
$n = $n->getFirstAncestor(
Node\Statement\FunctionDeclaration::class,
Node\MethodDeclaration::class,
Node\Expression\AnonymousFunctionCreationExpression::class,
Node\SourceFileNode::class
);
if ($n === null) {
$n = $node->getFirstAncestor(Node\Statement\ExpressionStatement::class)->getParent();
}
foreach ($n->getDescendantNodes() as $descendantNode) {
if ($descendantNode instanceof Node\Expression\Variable &&
if (
$descendantNode instanceof Node\Expression\Variable &&
$descendantNode->getName() === $node->getName()
) {
$locations[] = LocationFactory::fromNode($descendantNode);
@ -231,7 +240,7 @@ class TextDocument
foreach ($this->index->getReferenceUris($fqn) as $uri) {
$refDocumentPromises[] = $this->documentLoader->getOrLoad($uri);
}
$refDocuments = yield Promise\all($refDocumentPromises);
$refDocuments = (yield Promise\all($refDocumentPromises));
foreach ($refDocuments as $document) {
$refs = $document->getReferenceNodesByFqn($fqn);
if ($refs !== null) {
@ -257,7 +266,7 @@ class TextDocument
public function signatureHelp(TextDocumentIdentifier $textDocument, Position $position): Promise
{
return coroutine(function () use ($textDocument, $position) {
$document = yield $this->documentLoader->getOrLoad($textDocument->uri);
$document = (yield $this->documentLoader->getOrLoad($textDocument->uri));
return $this->signatureHelpProvider->getSignatureHelp($document, $position);
});
}
@ -273,7 +282,7 @@ class TextDocument
public function definition(TextDocumentIdentifier $textDocument, Position $position): Promise
{
return coroutine(function () use ($textDocument, $position) {
$document = yield $this->documentLoader->getOrLoad($textDocument->uri);
$document = (yield $this->documentLoader->getOrLoad($textDocument->uri));
$node = $document->getNodeAtPosition($position);
if ($node === null) {
return [];
@ -294,9 +303,9 @@ class TextDocument
yield waitForEvent($this->index, 'definition-added');
}
if (
$def === null
|| $def->symbolInformation === null
|| Uri\parse($def->symbolInformation->location->uri)['scheme'] === 'phpstubs'
$def === null ||
$def->symbolInformation === null ||
Uri\parse($def->symbolInformation->location->uri)['scheme'] === 'phpstubs'
) {
return [];
}
@ -314,7 +323,7 @@ class TextDocument
public function hover(TextDocumentIdentifier $textDocument, Position $position): Promise
{
return coroutine(function () use ($textDocument, $position) {
$document = yield $this->documentLoader->getOrLoad($textDocument->uri);
$document = (yield $this->documentLoader->getOrLoad($textDocument->uri));
// Find the node under the cursor
$node = $document->getNodeAtPosition($position);
if ($node === null) {
@ -365,10 +374,13 @@ class TextDocument
* @param CompletionContext|null $context The completion context
* @return Promise <CompletionItem[]|CompletionList>
*/
public function completion(TextDocumentIdentifier $textDocument, Position $position, CompletionContext $context = null): 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);
$document = (yield $this->documentLoader->getOrLoad($textDocument->uri));
return $this->completionProvider->provideCompletion($document, $position, $context);
});
}
@ -388,7 +400,7 @@ class TextDocument
public function xdefinition(TextDocumentIdentifier $textDocument, Position $position): Promise
{
return coroutine(function () use ($textDocument, $position) {
$document = yield $this->documentLoader->getOrLoad($textDocument->uri);
$document = (yield $this->documentLoader->getOrLoad($textDocument->uri));
$node = $document->getNodeAtPosition($position);
if ($node === null) {
return [];
@ -409,9 +421,9 @@ class TextDocument
yield waitForEvent($this->index, 'definition-added');
}
if (
$def === null
|| $def->symbolInformation === null
|| Uri\parse($def->symbolInformation->location->uri)['scheme'] === 'phpstubs'
$def === null ||
$def->symbolInformation === null ||
Uri\parse($def->symbolInformation->location->uri)['scheme'] === 'phpstubs'
) {
return [];
}

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Server;
@ -64,8 +64,15 @@ class Workspace
* @param \stdClass $composerLock The parsed composer.lock of the project, if any
* @param PhpDocumentLoader $documentLoader PhpDocumentLoader instance to load documents
*/
public function __construct(LanguageClient $client, ProjectIndex $projectIndex, DependenciesIndex $dependenciesIndex, Index $sourceIndex, \stdClass $composerLock = null, PhpDocumentLoader $documentLoader, \stdClass $composerJson = null)
{
public function __construct(
LanguageClient $client,
ProjectIndex $projectIndex,
DependenciesIndex $dependenciesIndex,
Index $sourceIndex,
\stdClass $composerLock = null,
PhpDocumentLoader $documentLoader,
\stdClass $composerJson = null
) {
$this->client = $client;
$this->sourceIndex = $sourceIndex;
$this->projectIndex = $projectIndex;
@ -148,9 +155,9 @@ class Workspace
$refInfos = [];
foreach ($refs as $uri => $fqns) {
foreach ($fqns as $fqn) {
$doc = yield $this->documentLoader->getOrLoad($uri);
$doc = (yield $this->documentLoader->getOrLoad($uri));
foreach ($doc->getReferenceNodesByFqn($fqn) as $node) {
$refInfo = new ReferenceInformation;
$refInfo = new ReferenceInformation();
$refInfo->reference = LocationFactory::fromNode($node);
$refInfo->symbol = $query;
$refInfos[] = $refInfo;
@ -170,7 +177,10 @@ class Workspace
return [];
}
$dependencyReferences = [];
foreach (array_merge($this->composerLock->packages, (array)$this->composerLock->{'packages-dev'}) as $package) {
foreach (
array_merge($this->composerLock->packages, (array) $this->composerLock->{'packages-dev'})
as $package
) {
$dependencyReferences[] = new DependencyReference($package);
}
return $dependencyReferences;

View File

@ -1,13 +1,10 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer;
use LanguageServer\Index\ReadableIndex;
use LanguageServerProtocol\{
Position,
SignatureHelp
};
use LanguageServerProtocol\{Position, SignatureHelp};
use Microsoft\PhpParser\Node;
use Sabre\Event\Promise;
use function Sabre\Event\coroutine;
@ -30,8 +27,11 @@ class SignatureHelpProvider
* @param ReadableIndex $index
* @param PhpDocumentLoader $documentLoader
*/
public function __construct(DefinitionResolver $definitionResolver, ReadableIndex $index, PhpDocumentLoader $documentLoader)
{
public function __construct(
DefinitionResolver $definitionResolver,
ReadableIndex $index,
PhpDocumentLoader $documentLoader
) {
$this->definitionResolver = $definitionResolver;
$this->index = $index;
$this->documentLoader = $documentLoader;
@ -52,7 +52,7 @@ class SignatureHelpProvider
$node = $doc->getNodeAtPosition($position);
// Find the definition of the item being called
list($def, $argumentExpressionList) = yield $this->getCallingInfo($node);
list($def, $argumentExpressionList) = (yield $this->getCallingInfo($node));
if (!$def || !$def->signatureInformation) {
return new SignatureHelp();

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer;
@ -32,11 +32,7 @@ class SignatureInformationFactory
{
$params = $this->createParameters($node);
$label = $this->createLabel($params);
return new SignatureInformation(
$label,
$params,
$this->definitionResolver->getDocumentationFromNode($node)
);
return new SignatureInformation($label, $params, $this->definitionResolver->getDocumentationFromNode($node));
}
/**

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer;
@ -20,6 +20,6 @@ class StderrLogger extends \Psr\Log\AbstractLogger implements \Psr\Log\LoggerInt
public function log($level, $message, array $context = array())
{
$contextStr = empty($context) ? '' : ' ' . \json_encode($context, \JSON_UNESCAPED_SLASHES);
\fwrite(\STDERR, \str_pad(\strtoupper((string)$level), 10) . $message . $contextStr . \PHP_EOL);
\fwrite(\STDERR, \str_pad(\strtoupper((string) $level), 10) . $message . $contextStr . \PHP_EOL);
}
}

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer;
@ -46,8 +46,13 @@ class TreeAnalyzer
* @param DefinitionResolver $definitionResolver
* @param string $uri
*/
public function __construct(PhpParser\Parser $parser, string $content, DocBlockFactory $docBlockFactory, DefinitionResolver $definitionResolver, string $uri)
{
public function __construct(
PhpParser\Parser $parser,
string $content,
DocBlockFactory $docBlockFactory,
DefinitionResolver $definitionResolver,
string $uri
) {
$this->parser = $parser;
$this->docBlockFactory = $docBlockFactory;
$this->definitionResolver = $definitionResolver;
@ -69,7 +74,11 @@ class TreeAnalyzer
{
// Get errors from the parser.
if (($error = PhpParser\DiagnosticsProvider::checkDiagnostics($node)) !== null) {
$range = PhpParser\PositionUtilities::getRangeFromPosition($error->start, $error->length, $this->sourceFileNode->fileContents);
$range = PhpParser\PositionUtilities::getRangeFromPosition(
$error->start,
$error->length,
$this->sourceFileNode->fileContents
);
switch ($error->kind) {
case PhpParser\DiagnosticKind::Error:
@ -150,24 +159,24 @@ class TreeAnalyzer
*/
private function collectDefinitionsAndReferences(Node $node)
{
$fqn = ($this->definitionResolver)::getDefinedFqn($node);
$fqn = $this->definitionResolver::getDefinedFqn($node);
// Only index definitions with an FQN (no variables)
if ($fqn !== null) {
$this->definitionNodes[$fqn] = $node;
$this->definitions[$fqn] = $this->definitionResolver->createDefinitionFromNode($node, $fqn);
} else {
$parent = $node->parent;
if (
(
// $node->parent instanceof Node\Expression\ScopedPropertyAccessExpression ||
($node instanceof Node\Expression\ScopedPropertyAccessExpression ||
$node instanceof Node\Expression\MemberAccessExpression)
&& !(
// $node->parent instanceof Node\Expression\ScopedPropertyAccessExpression ||
(($node instanceof Node\Expression\ScopedPropertyAccessExpression ||
$node instanceof Node\Expression\MemberAccessExpression) &&
!(
$node->parent instanceof Node\Expression\CallExpression ||
$node->memberName instanceof PhpParser\Token
))
|| ($parent instanceof Node\Statement\NamespaceDefinition && $parent->name !== null && $parent->name->getStart() === $node->getStart())
)) ||
($parent instanceof Node\Statement\NamespaceDefinition &&
$parent->name !== null &&
$parent->name->getStart() === $node->getStart())
) {
return;
}
@ -184,17 +193,17 @@ class TreeAnalyzer
if (!$classNode) {
return;
}
$fqn = (string)$classNode->getNamespacedName();
$fqn = (string) $classNode->getNamespacedName();
if (!$fqn) {
return;
}
} else if ($fqn === 'parent') {
} elseif ($fqn === 'parent') {
// Resolve parent keyword to the base class FQN
$classNode = $node->getFirstAncestor(Node\Statement\ClassDeclaration::class);
if (!$classNode || !$classNode->classBaseClause || !$classNode->classBaseClause->baseClass) {
return;
}
$fqn = (string)$classNode->classBaseClause->baseClass->getResolvedName();
$fqn = (string) $classNode->classBaseClause->baseClass->getResolvedName();
if (!$fqn) {
return;
}
@ -203,9 +212,11 @@ class TreeAnalyzer
$this->addReference($fqn, $node);
if (
$node instanceof Node\QualifiedName
&& ($node->isQualifiedName() || $node->parent instanceof Node\NamespaceUseClause)
&& !($parent instanceof Node\Statement\NamespaceDefinition && $parent->name->getStart() === $node->getStart()
$node instanceof Node\QualifiedName &&
($node->isQualifiedName() || $node->parent instanceof Node\NamespaceUseClause) &&
!(
$parent instanceof Node\Statement\NamespaceDefinition &&
$parent->name->getStart() === $node->getStart()
)
) {
// Add references for each referenced namespace
@ -219,12 +230,14 @@ class TreeAnalyzer
// Namespaced constant access and function calls also need to register a reference
// to the global version because PHP falls back to global at runtime
// http://php.net/manual/en/language.namespaces.fallback.php
if (ParserHelpers\isConstantFetch($node) ||
($parent instanceof Node\Expression\CallExpression
&& !(
if (
ParserHelpers\isConstantFetch($node) ||
($parent instanceof Node\Expression\CallExpression &&
!(
$node instanceof Node\Expression\ScopedPropertyAccessExpression ||
$node instanceof Node\Expression\MemberAccessExpression
))) {
))
) {
$parts = explode('\\', $fqn);
if (count($parts) > 1) {
$globalFqn = end($parts);

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer;
@ -74,7 +74,7 @@ function crash(Throwable $err)
*/
function timeout($seconds = 0): Promise
{
$promise = new Promise;
$promise = new Promise();
Loop\setTimeout([$promise, 'fulfill'], $seconds);
return $promise;
}
@ -88,7 +88,7 @@ function timeout($seconds = 0): Promise
*/
function waitForEvent(EmitterInterface $emitter, string $event): Promise
{
$p = new Promise;
$p = new Promise();
$emitter->once($event, [$p, 'fulfill']);
return $p;
}

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Tests;
@ -13,8 +13,8 @@ class ClientHandlerTest extends TestCase
{
public function testRequest()
{
$reader = new MockProtocolStream;
$writer = new MockProtocolStream;
$reader = new MockProtocolStream();
$writer = new MockProtocolStream();
$handler = new ClientHandler($reader, $writer);
$writer->once('message', function (Message $msg) use ($reader) {
// Respond to request
@ -22,9 +22,12 @@ class ClientHandlerTest extends TestCase
$reader->write(new Message(new AdvancedJsonRpc\SuccessResponse($msg->body->id, 'pong')));
}, 0);
});
$handler->request('testMethod', ['ping'])->then(function ($result) {
$this->assertEquals('pong', $result);
})->wait();
$handler
->request('testMethod', ['ping'])
->then(function ($result) {
$this->assertEquals('pong', $result);
})
->wait();
// No event listeners
$this->assertEquals([], $reader->listeners('message'));
$this->assertEquals([], $writer->listeners('message'));
@ -32,8 +35,8 @@ class ClientHandlerTest extends TestCase
public function testNotify()
{
$reader = new MockProtocolStream;
$writer = new MockProtocolStream;
$reader = new MockProtocolStream();
$writer = new MockProtocolStream();
$handler = new ClientHandler($reader, $writer);
$handler->notify('testMethod', ['ping'])->wait();
// No event listeners

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Tests;
@ -12,24 +12,27 @@ class DefinitionResolverTest extends TestCase
{
public function testCreateDefinitionFromNode()
{
$parser = new PhpParser\Parser;
$doc = new MockPhpDocument;
$parser = new PhpParser\Parser();
$doc = new MockPhpDocument();
$sourceFileNode = $parser->parseSourceFile("<?php\ndefine('TEST_DEFINE', true);", $doc->getUri());
$index = new Index;
$index = new Index();
$definitionResolver = new DefinitionResolver($index);
$def = $definitionResolver->createDefinitionFromNode($sourceFileNode->statementList[1]->expression, '\TEST_DEFINE');
$def = $definitionResolver->createDefinitionFromNode(
$sourceFileNode->statementList[1]->expression,
'\TEST_DEFINE'
);
$this->assertInstanceOf(\phpDocumentor\Reflection\Types\Boolean::class, $def->type);
}
public function testGetTypeFromNode()
{
$parser = new PhpParser\Parser;
$doc = new MockPhpDocument;
$parser = new PhpParser\Parser();
$doc = new MockPhpDocument();
$sourceFileNode = $parser->parseSourceFile("<?php\ndefine('TEST_DEFINE', true);", $doc->getUri());
$index = new Index;
$index = new Index();
$definitionResolver = new DefinitionResolver($index);
$type = $definitionResolver->getTypeFromNode($sourceFileNode->statementList[1]->expression);
@ -39,11 +42,11 @@ class DefinitionResolverTest extends TestCase
public function testGetDefinedFqnForIncompleteDefine()
{
// define('XXX') (only one argument) must not introduce a new symbol
$parser = new PhpParser\Parser;
$doc = new MockPhpDocument;
$parser = new PhpParser\Parser();
$doc = new MockPhpDocument();
$sourceFileNode = $parser->parseSourceFile("<?php\ndefine('TEST_DEFINE');", $doc->getUri());
$index = new Index;
$index = new Index();
$definitionResolver = new DefinitionResolver($index);
$fqn = $definitionResolver->getDefinedFqn($sourceFileNode->statementList[1]->expression);
@ -52,11 +55,11 @@ class DefinitionResolverTest extends TestCase
public function testGetDefinedFqnForDefine()
{
$parser = new PhpParser\Parser;
$doc = new MockPhpDocument;
$parser = new PhpParser\Parser();
$doc = new MockPhpDocument();
$sourceFileNode = $parser->parseSourceFile("<?php\ndefine('TEST_DEFINE', true);", $doc->getUri());
$index = new Index;
$index = new Index();
$definitionResolver = new DefinitionResolver($index);
$fqn = $definitionResolver->getDefinedFqn($sourceFileNode->statementList[1]->expression);

View File

@ -1,17 +1,13 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Tests\Diagnostics;
use PHPUnit\Framework\TestCase;
use phpDocumentor\Reflection\DocBlockFactory;
use LanguageServer\{
DefinitionResolver, TreeAnalyzer
};
use LanguageServer\{DefinitionResolver, TreeAnalyzer};
use LanguageServer\Index\{Index};
use LanguageServerProtocol\{
Diagnostic, DiagnosticSeverity, Position, Range
};
use LanguageServerProtocol\{Diagnostic, DiagnosticSeverity, Position, Range};
use function LanguageServer\pathToUri;
use Microsoft\PhpParser\Parser;
@ -29,7 +25,7 @@ class InvalidThisUsageTest extends TestCase
$parser = new Parser();
$docBlockFactory = DocBlockFactory::createInstance();
$index = new Index;
$index = new Index();
$definitionResolver = new DefinitionResolver($index);
$content = file_get_contents($path);
@ -64,18 +60,13 @@ class InvalidThisUsageTest extends TestCase
$diagnostics[0],
'$this can not be used in static methods.',
DiagnosticSeverity::ERROR,
new Range(
new Position(6, 15),
new Position(6, 20)
)
new Range(new Position(6, 15), new Position(6, 20))
);
}
public function testThisInMethodProducesNoError()
{
$diagnostics = $this->collectDiagnostics(
__DIR__ . '/../../fixtures/diagnostics/baselines/this_in_method.php'
);
$diagnostics = $this->collectDiagnostics(__DIR__ . '/../../fixtures/diagnostics/baselines/this_in_method.php');
$this->assertCount(0, $diagnostics);
}

View File

@ -10,19 +10,19 @@ class IndexTest extends TestCase
{
public function testGetSetMethodDefinition()
{
$index = new Index;
$index->setDefinition('SomeNamespace\SomeClass', new Definition);
$methodDefinition = new Definition;
$index = new Index();
$index->setDefinition('SomeNamespace\SomeClass', new Definition());
$methodDefinition = new Definition();
$methodFqn = 'SomeNamespace\SomeClass->someMethod()';
$index->setDefinition($methodFqn, $methodDefinition);
$index->setDefinition('SomeNamespace\SomeClass->someProperty', new Definition);
$index->setDefinition('SomeNamespace\SomeClass->someProperty', new Definition());
$this->assertSame($methodDefinition, $index->getDefinition($methodFqn));
}
public function testGetSetClassDefinition()
{
$index = new Index;
$definition = new Definition;
$index = new Index();
$definition = new Definition();
$fqn = 'SomeNamespace\SomeClass';
$index->setDefinition($fqn, $definition);
$this->assertSame($definition, $index->getDefinition($fqn));

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Tests;
@ -28,8 +28,8 @@ class LanguageServerTest extends TestCase
{
public function testInitialize()
{
$server = new LanguageServer(new MockProtocolStream, new MockProtocolStream);
$result = $server->initialize(new ClientCapabilities, __DIR__, getmypid())->wait();
$server = new LanguageServer(new MockProtocolStream(), new MockProtocolStream());
$result = $server->initialize(new ClientCapabilities(), __DIR__, getmypid())->wait();
$serverCapabilities = new ServerCapabilities();
$serverCapabilities->textDocumentSync = TextDocumentSyncKind::FULL;
@ -38,10 +38,10 @@ class LanguageServerTest extends TestCase
$serverCapabilities->definitionProvider = true;
$serverCapabilities->referencesProvider = true;
$serverCapabilities->hoverProvider = true;
$serverCapabilities->completionProvider = new CompletionOptions;
$serverCapabilities->completionProvider = new CompletionOptions();
$serverCapabilities->completionProvider->resolveProvider = false;
$serverCapabilities->completionProvider->triggerCharacters = ['$', '>'];
$serverCapabilities->signatureHelpProvider = new SignatureHelpOptions;
$serverCapabilities->signatureHelpProvider = new SignatureHelpOptions();
$serverCapabilities->signatureHelpProvider->triggerCharacters = ['(', ','];
$serverCapabilities->xworkspaceReferencesProvider = true;
$serverCapabilities->xdefinitionProvider = true;
@ -52,44 +52,51 @@ class LanguageServerTest extends TestCase
public function testIndexingWithDirectFileAccess()
{
$promise = new Promise;
$input = new MockProtocolStream;
$output = new MockProtocolStream;
$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)) {
} elseif (preg_match('/All \d+ PHP files parsed/', $msg->body->params->message)) {
$promise->fulfill();
}
}
});
$server = new LanguageServer($input, $output);
$capabilities = new ClientCapabilities;
$capabilities = new ClientCapabilities();
$server->initialize($capabilities, realpath(__DIR__ . '/../fixtures'), getmypid());
$promise->wait();
}
public function testIndexingWithFilesAndContentRequests()
{
$promise = new Promise;
$promise = new Promise();
$filesCalled = false;
$contentCalled = false;
$rootPath = realpath(__DIR__ . '/../fixtures');
$input = new MockProtocolStream;
$output = new MockProtocolStream;
$input = new MockProtocolStream();
$output = new MockProtocolStream();
$run = 1;
$output->on('message', function (Message $msg) use ($promise, $input, $rootPath, &$filesCalled, &$contentCalled, &$run) {
$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 = 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') {
} elseif ($msg->body->method === 'workspace/xfiles') {
// Files requested
$filesCalled = true;
$pattern = Path::makeAbsolute('**/*.php', $msg->body->params->base ?? $rootPath);
@ -98,20 +105,20 @@ class LanguageServerTest extends TestCase
$files[] = new TextDocumentIdentifier(pathToUri($path));
}
$input->write(new Message(new AdvancedJsonRpc\SuccessResponse($msg->body->id, $files)));
} else if ($msg->body->method === 'window/logMessage') {
} elseif ($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 (preg_match('/All \d+ PHP files parsed/', $msg->body->params->message)) {
} elseif (preg_match('/All \d+ PHP files parsed/', $msg->body->params->message)) {
$promise->fulfill();
}
}
});
$server = new LanguageServer($input, $output);
$capabilities = new ClientCapabilities;
$capabilities = new ClientCapabilities();
$capabilities->xfilesProvider = true;
$capabilities->xcontentProvider = true;
$server->initialize($capabilities, $rootPath, getmypid());

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Tests;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Tests;
@ -21,7 +21,7 @@ class MockProtocolStream extends Emitter implements ProtocolReader, ProtocolWrit
public function write(Message $msg): Promise
{
Loop\nextTick(function () use ($msg) {
$this->emit('message', [Message::parse((string)$msg)]);
$this->emit('message', [Message::parse((string) $msg)]);
});
return Promise\resolve(null);
}

View File

@ -1,13 +1,11 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Tests\Server\TextDocument;
use PHPUnit\Framework\TestCase;
use phpDocumentor\Reflection\DocBlockFactory;
use LanguageServer\{
DefinitionResolver, TreeAnalyzer
};
use LanguageServer\{DefinitionResolver, TreeAnalyzer};
use LanguageServer\Index\{Index};
use function LanguageServer\pathToUri;
use Microsoft\PhpParser;
@ -20,33 +18,42 @@ class DefinitionCollectorTest extends TestCase
$path = realpath(__DIR__ . '/../../fixtures/symbols.php');
$defNodes = $this->collectDefinitions($path);
$this->assertEquals([
'TestNamespace',
'TestNamespace\\TEST_CONST',
'TestNamespace\\TestClass',
'TestNamespace\\TestClass::TEST_CLASS_CONST',
'TestNamespace\\TestClass::$staticTestProperty',
'TestNamespace\\TestClass->testProperty',
'TestNamespace\\TestClass::staticTestMethod()',
'TestNamespace\\TestClass->testMethod()',
'TestNamespace\\TestTrait',
'TestNamespace\\TestInterface',
'TestNamespace\\test_function()',
'TestNamespace\\ChildClass',
'TestNamespace\\Example',
'TestNamespace\\Example->__construct()',
'TestNamespace\\Example->__destruct()',
'TestNamespace\\InnerNamespace',
'TestNamespace\\InnerNamespace\\InnerClass',
], array_keys($defNodes));
$this->assertEquals(
[
'TestNamespace',
'TestNamespace\\TEST_CONST',
'TestNamespace\\TestClass',
'TestNamespace\\TestClass::TEST_CLASS_CONST',
'TestNamespace\\TestClass::$staticTestProperty',
'TestNamespace\\TestClass->testProperty',
'TestNamespace\\TestClass::staticTestMethod()',
'TestNamespace\\TestClass->testMethod()',
'TestNamespace\\TestTrait',
'TestNamespace\\TestInterface',
'TestNamespace\\test_function()',
'TestNamespace\\ChildClass',
'TestNamespace\\Example',
'TestNamespace\\Example->__construct()',
'TestNamespace\\Example->__destruct()',
'TestNamespace\\InnerNamespace',
'TestNamespace\\InnerNamespace\\InnerClass'
],
array_keys($defNodes)
);
$this->assertInstanceOf(Node\ConstElement::class, $defNodes['TestNamespace\\TEST_CONST']);
$this->assertInstanceOf(Node\Statement\ClassDeclaration::class, $defNodes['TestNamespace\\TestClass']);
$this->assertInstanceOf(Node\ConstElement::class, $defNodes['TestNamespace\\TestClass::TEST_CLASS_CONST']);
// TODO - should we parse properties more strictly?
$this->assertInstanceOf(Node\Expression\Variable::class, $defNodes['TestNamespace\\TestClass::$staticTestProperty']);
$this->assertInstanceOf(
Node\Expression\Variable::class,
$defNodes['TestNamespace\\TestClass::$staticTestProperty']
);
$this->assertInstanceOf(Node\Expression\Variable::class, $defNodes['TestNamespace\\TestClass->testProperty']);
$this->assertInstanceOf(Node\MethodDeclaration::class, $defNodes['TestNamespace\\TestClass::staticTestMethod()']);
$this->assertInstanceOf(
Node\MethodDeclaration::class,
$defNodes['TestNamespace\\TestClass::staticTestMethod()']
);
$this->assertInstanceOf(Node\MethodDeclaration::class, $defNodes['TestNamespace\\TestClass->testMethod()']);
$this->assertInstanceOf(Node\Statement\TraitDeclaration::class, $defNodes['TestNamespace\\TestTrait']);
$this->assertInstanceOf(Node\Statement\InterfaceDeclaration::class, $defNodes['TestNamespace\\TestInterface']);
@ -55,7 +62,10 @@ class DefinitionCollectorTest extends TestCase
$this->assertInstanceOf(Node\Statement\ClassDeclaration::class, $defNodes['TestNamespace\\Example']);
$this->assertInstanceOf(Node\MethodDeclaration::class, $defNodes['TestNamespace\\Example->__construct()']);
$this->assertInstanceOf(Node\MethodDeclaration::class, $defNodes['TestNamespace\\Example->__destruct()']);
$this->assertInstanceOf(Node\Statement\ClassDeclaration::class, $defNodes['TestNamespace\\InnerNamespace\\InnerClass']);
$this->assertInstanceOf(
Node\Statement\ClassDeclaration::class,
$defNodes['TestNamespace\\InnerNamespace\\InnerClass']
);
}
public function testDoesNotCollectReferences()
@ -77,7 +87,7 @@ class DefinitionCollectorTest extends TestCase
$parser = new PhpParser\Parser();
$docBlockFactory = DocBlockFactory::createInstance();
$index = new Index;
$index = new Index();
$definitionResolver = new DefinitionResolver($index);
$content = file_get_contents($path);

View File

@ -1,15 +1,11 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Tests\Server;
use LanguageServer\{
PhpDocument, PhpDocumentLoader, Project, DefinitionResolver
};
use LanguageServer\{PhpDocument, PhpDocumentLoader, Project, DefinitionResolver};
use LanguageServer\ContentRetriever\FileSystemContentRetriever;
use LanguageServer\Index\{
DependenciesIndex, Index, ProjectIndex
};
use LanguageServer\Index\{DependenciesIndex, Index, ProjectIndex};
use PHPUnit\Framework\TestCase;
use function LanguageServer\pathToUri;
@ -22,9 +18,9 @@ class PhpDocumentLoaderTest extends TestCase
public function setUp()
{
$projectIndex = new ProjectIndex(new Index, new DependenciesIndex);
$projectIndex = new ProjectIndex(new Index(), new DependenciesIndex());
$this->loader = new PhpDocumentLoader(
new FileSystemContentRetriever,
new FileSystemContentRetriever(),
$projectIndex,
new DefinitionResolver($projectIndex)
);

View File

@ -1,17 +1,11 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Tests\Server;
use LanguageServer\{
PhpDocument, DefinitionResolver
};
use LanguageServer\Index\{
Index
};
use LanguageServerProtocol\{
Position
};
use LanguageServer\{PhpDocument, DefinitionResolver};
use LanguageServer\Index\{Index};
use LanguageServerProtocol\{Position};
use Microsoft\PhpParser;
use Microsoft\PhpParser\Node;
use phpDocumentor\Reflection\DocBlockFactory;
@ -24,7 +18,7 @@ class PhpDocumentTest extends TestCase
{
$parser = new PhpParser\Parser();
$docBlockFactory = DocBlockFactory::createInstance();
$index = new Index;
$index = new Index();
$definitionResolver = new DefinitionResolver($index);
return new PhpDocument($uri, $content, $index, $parser, $docBlockFactory, $definitionResolver);
}
@ -41,7 +35,7 @@ class PhpDocumentTest extends TestCase
$document = $this->createDocument('whatever', "<?php\n$\$a = new SomeClass;");
$node = $document->getNodeAtPosition(new Position(1, 13));
$this->assertQualifiedName($node);
$this->assertEquals('SomeClass', (string)$node);
$this->assertEquals('SomeClass', (string) $node);
}
private function assertQualifiedName($node)

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Tests;
@ -20,13 +20,13 @@ class ProtocolStreamReaderTest extends TestCase
$reader->on('message', function (Message $message) use (&$msg) {
$msg = $message;
});
$ret = fwrite($writeHandle, (string)new Message(new RequestBody(1, 'aMethod', ['arg' => 'Hello World'])));
$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);
$this->assertEquals((object) ['arg' => 'Hello World'], $msg->body->params);
}
}

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Tests;
@ -20,7 +20,7 @@ class ProtocolStreamWriterTest extends TestCase
$writer = new ProtocolStreamWriter($writeHandle);
$msg = new Message(new RequestBody(1, 'aMethod', ['arg' => str_repeat('X', 100000)]));
$msgString = (string)$msg;
$msgString = (string) $msg;
$promise = $writer->write($msg);

View File

@ -1,13 +1,11 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Tests\Server;
use PHPUnit\Framework\TestCase;
use LanguageServer\Tests\MockProtocolStream;
use LanguageServer\{
Server, LanguageClient, PhpDocumentLoader, DefinitionResolver
};
use LanguageServer\{Server, LanguageClient, PhpDocumentLoader, DefinitionResolver};
use LanguageServer\Index\{ProjectIndex, DependenciesIndex, Index};
use LanguageServer\ContentRetriever\FileSystemContentRetriever;
use LanguageServerProtocol\{Position, Location, Range};
@ -46,22 +44,38 @@ abstract class ServerTestCase extends TestCase
public function setUp()
{
$sourceIndex = new Index;
$dependenciesIndex = new DependenciesIndex;
$projectIndex = new ProjectIndex($sourceIndex, $dependenciesIndex);
$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);
$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'));
$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'));
$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();
@ -71,156 +85,229 @@ abstract class ServerTestCase extends TestCase
// @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))),
'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))),
'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};
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)))
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;
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;
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;
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)
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)))
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;
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;
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)))
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();
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))),
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)))
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)))
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;
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;
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;
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)
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)))
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;
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;
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)))
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();
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))),
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)))
]
];

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,11 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Tests\Server\TextDocument\Definition;
use LanguageServer\Tests\MockProtocolStream;
use LanguageServer\Tests\Server\ServerTestCase;
use LanguageServer\{
Server, LanguageClient, PhpDocumentLoader, DefinitionResolver
};
use LanguageServer\{Server, LanguageClient, PhpDocumentLoader, DefinitionResolver};
use LanguageServer\Index\{Index, ProjectIndex, DependenciesIndex};
use LanguageServer\ContentRetriever\FileSystemContentRetriever;
use LanguageServerProtocol\{TextDocumentIdentifier, Position, Range, Location};
@ -16,11 +14,11 @@ class GlobalFallbackTest extends ServerTestCase
{
public function setUp()
{
$projectIndex = new ProjectIndex(new Index, new DependenciesIndex);
$projectIndex = new ProjectIndex(new Index(), new DependenciesIndex());
$projectIndex->setComplete();
$client = new LanguageClient(new MockProtocolStream, new MockProtocolStream);
$client = new LanguageClient(new MockProtocolStream(), new MockProtocolStream());
$definitionResolver = new DefinitionResolver($projectIndex);
$contentRetriever = new FileSystemContentRetriever;
$contentRetriever = new FileSystemContentRetriever();
$loader = new PhpDocumentLoader($contentRetriever, $projectIndex, $definitionResolver);
$this->textDocument = new Server\TextDocument($loader, $definitionResolver, $client, $projectIndex);
$loader->open('global_fallback', file_get_contents(__DIR__ . '/../../../../fixtures/global_fallback.php'));
@ -31,10 +29,9 @@ 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)
)->wait();
$result = $this->textDocument
->definition(new TextDocumentIdentifier('global_fallback'), new Position(9, 16))
->wait();
$this->assertEquals([], $result);
}
@ -42,21 +39,25 @@ class GlobalFallbackTest extends ServerTestCase
{
// 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);
$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
);
}
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);
$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
);
}
}

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Tests\Server\TextDocument\Definition;
@ -12,20 +12,24 @@ class GlobalTest extends ServerTestCase
public function testDefinitionFileBeginning()
{
// |<?php
$result = $this->textDocument->definition(
new TextDocumentIdentifier(pathToUri(realpath(__DIR__ . '/../../../../fixtures/references.php'))),
new Position(0, 0)
)->wait();
$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(1, 0)
)->wait();
$result = $this->textDocument
->definition(
new TextDocumentIdentifier(pathToUri(realpath(__DIR__ . '/../../../../fixtures/references.php'))),
new Position(1, 0)
)
->wait();
$this->assertEquals([], $result);
}
@ -34,10 +38,9 @@ class GlobalTest extends ServerTestCase
// 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();
$result = $this->textDocument
->definition(new TextDocumentIdentifier($reference->uri), $reference->range->start)
->wait();
$this->assertEquals($this->getDefinitionLocation('TestClass'), $result);
}
@ -46,10 +49,9 @@ class GlobalTest extends ServerTestCase
// $obj = new TestClass();
// Get definition for TestClass
$reference = $this->getReferenceLocations('TestClass')[1];
$result = $this->textDocument->definition(
new TextDocumentIdentifier($reference->uri),
$reference->range->start
)->wait();
$result = $this->textDocument
->definition(new TextDocumentIdentifier($reference->uri), $reference->range->start)
->wait();
$this->assertEquals($this->getDefinitionLocation('TestClass'), $result);
}
@ -58,10 +60,9 @@ class GlobalTest extends ServerTestCase
// TestClass::staticTestMethod();
// Get definition for TestClass
$reference = $this->getReferenceLocations('TestClass')[2];
$result = $this->textDocument->definition(
new TextDocumentIdentifier($reference->uri),
$reference->range->start
)->wait();
$result = $this->textDocument
->definition(new TextDocumentIdentifier($reference->uri), $reference->range->start)
->wait();
$this->assertEquals($this->getDefinitionLocation('TestClass'), $result);
}
@ -70,10 +71,9 @@ class GlobalTest extends ServerTestCase
// echo TestClass::$staticTestProperty;
// Get definition for TestClass
$reference = $this->getReferenceLocations('TestClass')[3];
$result = $this->textDocument->definition(
new TextDocumentIdentifier($reference->uri),
$reference->range->start
)->wait();
$result = $this->textDocument
->definition(new TextDocumentIdentifier($reference->uri), $reference->range->start)
->wait();
$this->assertEquals($this->getDefinitionLocation('TestClass'), $result);
}
@ -82,10 +82,9 @@ class GlobalTest extends ServerTestCase
// 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();
$result = $this->textDocument
->definition(new TextDocumentIdentifier($reference->uri), $reference->range->start)
->wait();
$this->assertEquals($this->getDefinitionLocation('TestClass'), $result);
}
@ -94,10 +93,9 @@ 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
)->wait();
$result = $this->textDocument
->definition(new TextDocumentIdentifier($reference->uri), $reference->range->start)
->wait();
$this->assertEquals($this->getDefinitionLocation('TestInterface'), $result);
}
@ -106,10 +104,9 @@ 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
)->wait();
$result = $this->textDocument
->definition(new TextDocumentIdentifier($reference->uri), $reference->range->end)
->wait();
$this->assertEquals($this->getDefinitionLocation('TestClass::TEST_CLASS_CONST'), $result);
}
@ -118,10 +115,9 @@ 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
)->wait();
$result = $this->textDocument
->definition(new TextDocumentIdentifier($reference->uri), $reference->range->end)
->wait();
$this->assertEquals($this->getDefinitionLocation('TestClass::TEST_CLASS_CONST'), $result);
}
@ -130,10 +126,9 @@ 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
)->wait();
$result = $this->textDocument
->definition(new TextDocumentIdentifier($reference->uri), $reference->range->start)
->wait();
$this->assertEquals($this->getDefinitionLocation('TEST_CONST'), $result);
}
@ -142,10 +137,9 @@ 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
)->wait();
$result = $this->textDocument
->definition(new TextDocumentIdentifier($reference->uri), $reference->range->end)
->wait();
$this->assertEquals($this->getDefinitionLocation('TestClass::staticTestMethod()'), $result);
}
@ -154,10 +148,9 @@ 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
)->wait();
$result = $this->textDocument
->definition(new TextDocumentIdentifier($reference->uri), $reference->range->end)
->wait();
$this->assertEquals($this->getDefinitionLocation('TestClass::staticTestProperty'), $result);
}
@ -166,10 +159,9 @@ 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
)->wait();
$result = $this->textDocument
->definition(new TextDocumentIdentifier($reference->uri), $reference->range->end)
->wait();
$this->assertEquals($this->getDefinitionLocation('TestClass::testMethod()'), $result);
}
@ -178,10 +170,9 @@ class GlobalTest extends ServerTestCase
// $child->testMethod();
// Get definition for testMethod
$reference = $this->getReferenceLocations('TestClass::testMethod()')[2];
$result = $this->textDocument->definition(
new TextDocumentIdentifier($reference->uri),
$reference->range->end
)->wait();
$result = $this->textDocument
->definition(new TextDocumentIdentifier($reference->uri), $reference->range->end)
->wait();
$this->assertEquals($this->getDefinitionLocation('TestClass::testMethod()'), $result);
}
@ -190,10 +181,9 @@ 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
)->wait();
$result = $this->textDocument
->definition(new TextDocumentIdentifier($reference->uri), $reference->range->end)
->wait();
$this->assertEquals($this->getDefinitionLocation('TestClass::testProperty'), $result);
}
@ -202,10 +192,9 @@ 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
)->wait();
$result = $this->textDocument
->definition(new TextDocumentIdentifier($reference->uri), $reference->range->end)
->wait();
$this->assertEquals($this->getDefinitionLocation('TestClass::testProperty'), $result);
}
@ -214,10 +203,7 @@ 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)
)->wait();
$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);
}
@ -226,10 +212,9 @@ class GlobalTest extends ServerTestCase
// 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();
$result = $this->textDocument
->definition(new TextDocumentIdentifier($reference->uri), $reference->range->start)
->wait();
$this->assertEquals($this->getDefinitionLocation('TestClass'), $result);
}
@ -238,10 +223,9 @@ class GlobalTest extends ServerTestCase
// 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();
$result = $this->textDocument
->definition(new TextDocumentIdentifier($reference->uri), $reference->range->start)
->wait();
$this->assertEquals($this->getDefinitionLocation('TestClass'), $result);
}
@ -250,10 +234,9 @@ 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
)->wait();
$result = $this->textDocument
->definition(new TextDocumentIdentifier($reference->uri), $reference->range->start)
->wait();
$this->assertEquals($this->getDefinitionLocation('TestInterface'), $result);
}
@ -262,10 +245,7 @@ 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)
)->wait();
$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);
}
@ -274,10 +254,7 @@ 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)
)->wait();
$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);
}
@ -286,10 +263,9 @@ 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
)->wait();
$result = $this->textDocument
->definition(new TextDocumentIdentifier($reference->uri), $reference->range->start)
->wait();
$this->assertEquals($this->getDefinitionLocation('test_function()'), $result);
}
@ -298,10 +274,9 @@ 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
)->wait();
$result = $this->textDocument
->definition(new TextDocumentIdentifier($reference->uri), $reference->range->start)
->wait();
$this->assertEquals($this->getDefinitionLocation('test_function()'), $result);
}
@ -310,10 +285,9 @@ 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
)->wait();
$result = $this->textDocument
->definition(new TextDocumentIdentifier($reference->uri), $reference->range->start)
->wait();
$this->assertEquals($this->getDefinitionLocation('TestInterface'), $result);
}
@ -322,10 +296,9 @@ class GlobalTest extends ServerTestCase
// $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();
$result = $this->textDocument
->definition(new TextDocumentIdentifier($reference->uri), $reference->range->end)
->wait();
$this->assertEquals($this->getDefinitionLocation('TestClass::testMethod()'), $result);
}
@ -334,10 +307,9 @@ class GlobalTest extends ServerTestCase
// 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();
$result = $this->textDocument
->definition(new TextDocumentIdentifier($reference->uri), $reference->range->end)
->wait();
$this->assertEquals($this->getDefinitionLocation('TestClass::testProperty'), $result);
}
}

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Tests\Server\TextDocument\Definition;
@ -23,10 +23,9 @@ 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
)->wait();
$result = $this->textDocument
->definition(new TextDocumentIdentifier($reference->uri), $reference->range->start)
->wait();
$this->assertEquals($this->getDefinitionLocation('TEST_CONST'), $result);
}
@ -35,10 +34,9 @@ class NamespacedTest extends GlobalTest
// use TestNamespace\TestClass;
// Get definition for TestClass
$reference = $this->getReferenceLocations('TestClass')[7];
$result = $this->textDocument->definition(
new TextDocumentIdentifier($reference->uri),
$reference->range->start
)->wait();
$result = $this->textDocument
->definition(new TextDocumentIdentifier($reference->uri), $reference->range->start)
->wait();
$this->assertEquals($this->getDefinitionLocation('TestClass'), $result);
}
@ -47,10 +45,9 @@ class NamespacedTest extends GlobalTest
// 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();
$result = $this->textDocument
->definition(new TextDocumentIdentifier($reference->uri), $reference->range->start)
->wait();
$this->assertEquals($this->getDefinitionLocation('TestClass'), $result);
}
}

View File

@ -1,30 +1,23 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Tests\Server\TextDocument;
use PHPUnit\Framework\TestCase;
use LanguageServer\Tests\MockProtocolStream;
use LanguageServer\{
Server, LanguageClient, PhpDocumentLoader, DefinitionResolver
};
use LanguageServer\{Server, LanguageClient, PhpDocumentLoader, DefinitionResolver};
use LanguageServer\ContentRetriever\FileSystemContentRetriever;
use LanguageServer\Index\{Index, ProjectIndex, DependenciesIndex};
use LanguageServerProtocol\{
VersionedTextDocumentIdentifier,
TextDocumentContentChangeEvent,
Range,
Position
};
use LanguageServerProtocol\{VersionedTextDocumentIdentifier, TextDocumentContentChangeEvent, Range, Position};
class DidChangeTest extends TestCase
{
public function test()
{
$projectIndex = new ProjectIndex(new Index, new DependenciesIndex);
$client = new LanguageClient(new MockProtocolStream, new MockProtocolStream);
$projectIndex = new ProjectIndex(new Index(), new DependenciesIndex());
$client = new LanguageClient(new MockProtocolStream(), new MockProtocolStream());
$definitionResolver = new DefinitionResolver($projectIndex);
$loader = new PhpDocumentLoader(new FileSystemContentRetriever, $projectIndex, $definitionResolver);
$loader = new PhpDocumentLoader(new FileSystemContentRetriever(), $projectIndex, $definitionResolver);
$textDocument = new Server\TextDocument($loader, $definitionResolver, $client, $projectIndex);
$phpDocument = $loader->open('whatever', "<?php\necho 'Hello, World'\n");

View File

@ -1,13 +1,11 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Tests\Server\TextDocument;
use PHPUnit\Framework\TestCase;
use LanguageServer\Tests\MockProtocolStream;
use LanguageServer\{
Server, LanguageClient, PhpDocumentLoader, DefinitionResolver
};
use LanguageServer\{Server, LanguageClient, PhpDocumentLoader, DefinitionResolver};
use LanguageServer\ContentRetriever\FileSystemContentRetriever;
use LanguageServer\Index\{Index, ProjectIndex, DependenciesIndex};
use LanguageServerProtocol\{TextDocumentItem, TextDocumentIdentifier};
@ -16,10 +14,10 @@ class DidCloseTest extends TestCase
{
public function test()
{
$projectIndex = new ProjectIndex(new Index, new DependenciesIndex);
$client = new LanguageClient(new MockProtocolStream, new MockProtocolStream);
$projectIndex = new ProjectIndex(new Index(), new DependenciesIndex());
$client = new LanguageClient(new MockProtocolStream(), new MockProtocolStream());
$definitionResolver = new DefinitionResolver($projectIndex);
$loader = new PhpDocumentLoader(new FileSystemContentRetriever, $projectIndex, $definitionResolver);
$loader = new PhpDocumentLoader(new FileSystemContentRetriever(), $projectIndex, $definitionResolver);
$textDocument = new Server\TextDocument($loader, $definitionResolver, $client, $projectIndex);
$phpDocument = $loader->open('whatever', "<?php\necho 'Hello, World'\n");

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Tests\Server\TextDocument;
@ -17,25 +17,113 @@ class DocumentSymbolTest extends ServerTestCase
$uri = pathToUri(realpath(__DIR__ . '/../../../fixtures/symbols.php'));
$result = $this->textDocument->documentSymbol(new TextDocumentIdentifier($uri))->wait();
// @codingStandardsIgnoreStart
$this->assertEquals([
new SymbolInformation('TestNamespace', SymbolKind::NAMESPACE, $this->getDefinitionLocation('TestNamespace'), ''),
new SymbolInformation('TEST_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestNamespace\\TEST_CONST'), 'TestNamespace'),
new SymbolInformation('TestClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\TestClass'), 'TestNamespace'),
new SymbolInformation('TEST_CLASS_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestNamespace\\TestClass::TEST_CLASS_CONST'), 'TestNamespace\\TestClass'),
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);
$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
}
}

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Tests\Server\TextDocument;
@ -16,19 +16,29 @@ class HoverTest extends ServerTestCase
// $obj = new TestClass();
// Get hover for TestClass
$reference = $this->getReferenceLocations('TestClass')[1];
$result = $this->textDocument->hover(
new TextDocumentIdentifier($reference->uri),
$reference->range->start
)->wait();
$this->assertEquals(new Hover([
new MarkedString('php', "<?php\nclass TestClass implements TestInterface"),
'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.'
], $reference->range), $result);
$result = $this->textDocument
->hover(new TextDocumentIdentifier($reference->uri), $reference->range->start)
->wait();
$this->assertEquals(
new Hover(
[
new MarkedString('php', "<?php\nclass TestClass implements TestInterface"),
'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.'
],
$reference->range
),
$result
);
}
public function testHoverForClassLikeDefinition()
@ -36,19 +46,29 @@ class HoverTest extends ServerTestCase
// 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', "<?php\nclass TestClass implements TestInterface"),
'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.'
], $definition->range), $result);
$result = $this->textDocument
->hover(new TextDocumentIdentifier($definition->uri), $definition->range->start)
->wait();
$this->assertEquals(
new Hover(
[
new MarkedString('php', "<?php\nclass TestClass implements TestInterface"),
'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.'
],
$definition->range
),
$result
);
}
public function testHoverForMethod()
@ -56,14 +76,19 @@ class HoverTest extends ServerTestCase
// $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', "<?php\npublic function testMethod(\$testParameter): TestInterface"),
'Non culpa nostrud mollit esse sunt laboris in irure ullamco cupidatat amet.'
], $reference->range), $result);
$result = $this->textDocument
->hover(new TextDocumentIdentifier($reference->uri), $reference->range->end)
->wait();
$this->assertEquals(
new Hover(
[
new MarkedString('php', "<?php\npublic function testMethod(\$testParameter): TestInterface"),
'Non culpa nostrud mollit esse sunt laboris in irure ullamco cupidatat amet.'
],
$reference->range
),
$result
);
}
public function testHoverForProperty()
@ -71,14 +96,19 @@ class HoverTest extends ServerTestCase
// 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', "<?php\npublic \$testProperty;"),
'Reprehenderit magna velit mollit ipsum do.'
], $reference->range), $result);
$result = $this->textDocument
->hover(new TextDocumentIdentifier($reference->uri), $reference->range->end)
->wait();
$this->assertEquals(
new Hover(
[
new MarkedString('php', "<?php\npublic \$testProperty;"),
'Reprehenderit magna velit mollit ipsum do.'
],
$reference->range
),
$result
);
}
public function testHoverForStaticMethod()
@ -86,14 +116,19 @@ class HoverTest extends ServerTestCase
// 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', "<?php\npublic static function staticTestMethod()"),
'Do magna consequat veniam minim proident eiusmod incididunt aute proident.'
], $reference->range), $result);
$result = $this->textDocument
->hover(new TextDocumentIdentifier($reference->uri), $reference->range->end)
->wait();
$this->assertEquals(
new Hover(
[
new MarkedString('php', "<?php\npublic static function staticTestMethod()"),
'Do magna consequat veniam minim proident eiusmod incididunt aute proident.'
],
$reference->range
),
$result
);
}
public function testHoverForStaticProperty()
@ -101,14 +136,19 @@ class HoverTest extends ServerTestCase
// 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', "<?php\npublic static \$staticTestProperty;"),
'Lorem excepteur officia sit anim velit veniam enim.'
], $reference->range), $result);
$result = $this->textDocument
->hover(new TextDocumentIdentifier($reference->uri), $reference->range->end)
->wait();
$this->assertEquals(
new Hover(
[
new MarkedString('php', "<?php\npublic static \$staticTestProperty;"),
'Lorem excepteur officia sit anim velit veniam enim.'
],
$reference->range
),
$result
);
}
public function testHoverForClassConstant()
@ -116,14 +156,19 @@ class HoverTest extends ServerTestCase
// 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', "<?php\nconst TEST_CLASS_CONST = 123;"),
'Anim labore veniam consectetur laboris minim quis aute aute esse nulla ad.'
], $reference->range), $result);
$result = $this->textDocument
->hover(new TextDocumentIdentifier($reference->uri), $reference->range->end)
->wait();
$this->assertEquals(
new Hover(
[
new MarkedString('php', "<?php\nconst TEST_CLASS_CONST = 123;"),
'Anim labore veniam consectetur laboris minim quis aute aute esse nulla ad.'
],
$reference->range
),
$result
);
}
public function testHoverForFunction()
@ -131,14 +176,19 @@ class HoverTest extends ServerTestCase
// 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', "<?php\nfunction test_function()"),
'Officia aliquip adipisicing et nulla et laboris dolore labore.'
], $reference->range), $result);
$result = $this->textDocument
->hover(new TextDocumentIdentifier($reference->uri), $reference->range->end)
->wait();
$this->assertEquals(
new Hover(
[
new MarkedString('php', "<?php\nfunction test_function()"),
'Officia aliquip adipisicing et nulla et laboris dolore labore.'
],
$reference->range
),
$result
);
}
public function testHoverForConstant()
@ -146,14 +196,19 @@ class HoverTest extends ServerTestCase
// 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', "<?php\nconst TEST_CONST = 123;"),
'Esse commodo excepteur pariatur Lorem est aute incididunt reprehenderit.'
], $reference->range), $result);
$result = $this->textDocument
->hover(new TextDocumentIdentifier($reference->uri), $reference->range->end)
->wait();
$this->assertEquals(
new Hover(
[
new MarkedString('php', "<?php\nconst TEST_CONST = 123;"),
'Esse commodo excepteur pariatur Lorem est aute incididunt reprehenderit.'
],
$reference->range
),
$result
);
}
public function testHoverForGlobalConstant()
@ -161,15 +216,20 @@ class HoverTest extends ServerTestCase
// 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();
$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', "<?php\ndefine('TEST_DEFINE_CONSTANT', false)"),
'Lorem ipsum dolor sit amet, consectetur.'
], $reference->range), $result);
$this->assertEquals(
new Hover(
[
new MarkedString('php', "<?php\ndefine('TEST_DEFINE_CONSTANT', false)"),
'Lorem ipsum dolor sit amet, consectetur.'
],
$reference->range
),
$result
);
}
public function testHoverForVariable()
@ -178,10 +238,13 @@ class HoverTest extends ServerTestCase
// 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', "<?php\n\$var = 123")],
new Range(new Position(13, 5), new Position(13, 9))
), $result);
$this->assertEquals(
new Hover(
[new MarkedString('php', "<?php\n\$var = 123")],
new Range(new Position(13, 5), new Position(13, 9))
),
$result
);
}
public function testHoverForParam()
@ -190,13 +253,16 @@ class HoverTest extends ServerTestCase
// Get hover for $param
$uri = pathToUri(realpath(__DIR__ . '/../../../fixtures/references.php'));
$result = $this->textDocument->hover(new TextDocumentIdentifier($uri), new Position(22, 11))->wait();
$this->assertEquals(new Hover(
[
new MarkedString('php', "<?php\nTestClass \$param"),
'Adipisicing non non cillum sint incididunt cillum enim mollit.'
],
new Range(new Position(22, 9), new Position(22, 15))
), $result);
$this->assertEquals(
new Hover(
[
new MarkedString('php', "<?php\nTestClass \$param"),
'Adipisicing non non cillum sint incididunt cillum enim mollit.'
],
new Range(new Position(22, 9), new Position(22, 15))
),
$result
);
}
public function testHoverForThis()
@ -205,14 +271,25 @@ class HoverTest extends ServerTestCase
// Get hover for $this
$uri = pathToUri(realpath(__DIR__ . '/../../../fixtures/global_symbols.php'));
$result = $this->textDocument->hover(new TextDocumentIdentifier($uri), new Position(59, 11))->wait();
$this->assertEquals(new Hover([
new MarkedString('php', "<?php\nclass TestClass implements TestInterface"),
'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.'
], new Range(new Position(59, 8), new Position(59, 13))), $result);
$this->assertEquals(
new Hover(
[
new MarkedString('php', "<?php\nclass TestClass implements TestInterface"),
'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.'
],
new Range(new Position(59, 8), new Position(59, 13))
),
$result
);
}
}

View File

@ -1,13 +1,11 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Tests\Server\TextDocument;
use PHPUnit\Framework\TestCase;
use LanguageServer\Tests\MockProtocolStream;
use LanguageServer\{
Server, Client, LanguageClient, ClientHandler, PhpDocumentLoader, DefinitionResolver
};
use LanguageServer\{Server, Client, LanguageClient, ClientHandler, PhpDocumentLoader, DefinitionResolver};
use LanguageServer\Index\{Index, ProjectIndex, DependenciesIndex};
use LanguageServer\ContentRetriever\FileSystemContentRetriever;
use LanguageServerProtocol\{TextDocumentItem, DiagnosticSeverity};
@ -25,12 +23,15 @@ class ParseErrorsTest extends TestCase
public function setUp()
{
$client = new LanguageClient(new MockProtocolStream, new MockProtocolStream);
$client = new LanguageClient(new MockProtocolStream(), new MockProtocolStream());
$client->textDocument = new class($this->args) extends Client\TextDocument {
private $args;
public function __construct(&$args)
{
parent::__construct(new ClientHandler(new MockProtocolStream, new MockProtocolStream), new JsonMapper);
parent::__construct(
new ClientHandler(new MockProtocolStream(), new MockProtocolStream()),
new JsonMapper()
);
$this->args = &$args;
}
public function publishDiagnostics(string $uri, array $diagnostics): Promise
@ -39,9 +40,9 @@ class ParseErrorsTest extends TestCase
return Promise\resolve(null);
}
};
$projectIndex = new ProjectIndex(new Index, new DependenciesIndex);
$projectIndex = new ProjectIndex(new Index(), new DependenciesIndex());
$definitionResolver = new DefinitionResolver($projectIndex);
$loader = new PhpDocumentLoader(new FileSystemContentRetriever, $projectIndex, $definitionResolver);
$loader = new PhpDocumentLoader(new FileSystemContentRetriever(), $projectIndex, $definitionResolver);
$this->textDocument = new Server\TextDocument($loader, $definitionResolver, $client, $projectIndex);
}
@ -58,97 +59,107 @@ class ParseErrorsTest extends TestCase
public function testParseErrorsArePublishedAsDiagnostics()
{
$this->openFile(__DIR__ . '/../../../fixtures/invalid_file.php');
$this->assertEquals([
'whatever',
[[
'range' => [
'start' => [
'line' => 2,
'character' => 9
],
'end' => [
'line' => 2,
'character' => 9
]
],
'severity' => DiagnosticSeverity::ERROR,
'code' => null,
'source' => 'php',
'message' => "'Name' expected."
],
$this->assertEquals(
[
'range' => [
'start' => [
'line' => 2,
'character' => 9
'whatever',
[
[
'range' => [
'start' => [
'line' => 2,
'character' => 9
],
'end' => [
'line' => 2,
'character' => 9
]
],
'severity' => DiagnosticSeverity::ERROR,
'code' => null,
'source' => 'php',
'message' => "'Name' expected."
],
'end' => [
'line' => 2,
'character' => 9
[
'range' => [
'start' => [
'line' => 2,
'character' => 9
],
'end' => [
'line' => 2,
'character' => 9
]
],
'severity' => DiagnosticSeverity::ERROR,
'code' => null,
'source' => 'php',
'message' => "'{' expected."
],
[
'range' => [
'start' => [
'line' => 2,
'character' => 9
],
'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."
]
],
'severity' => DiagnosticSeverity::ERROR,
'code' => null,
'source' => 'php',
'message' => "'{' expected."
]
],
[
'range' => [
'start' => [
'line' => 2,
'character' => 9
],
'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));
json_decode(json_encode($this->args), 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
],
'end' => [
'line' => 4,
'character' => 0
$this->assertEquals(
[
'whatever',
[
[
'range' => [
'start' => [
'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"
]
],
'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));
]
],
json_decode(json_encode($this->args), true)
);
}
}

View File

@ -1,18 +1,12 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Tests\Server\TextDocument\References;
use LanguageServer\{
LanguageClient, PhpDocumentLoader, Server, DefinitionResolver
};
use LanguageServer\{LanguageClient, PhpDocumentLoader, Server, DefinitionResolver};
use LanguageServer\ContentRetriever\FileSystemContentRetriever;
use LanguageServer\Index\{
DependenciesIndex, Index, ProjectIndex
};
use LanguageServerProtocol\{
Location, Position, Range, ReferenceContext, TextDocumentIdentifier
};
use LanguageServer\Index\{DependenciesIndex, Index, ProjectIndex};
use LanguageServerProtocol\{Location, Position, Range, ReferenceContext, TextDocumentIdentifier};
use LanguageServer\Tests\MockProtocolStream;
use LanguageServer\Tests\Server\ServerTestCase;
@ -20,25 +14,38 @@ class GlobalFallbackTest extends ServerTestCase
{
public function setUp()
{
$projectIndex = new ProjectIndex(new Index, new DependenciesIndex);
$projectIndex = new ProjectIndex(new Index(), new 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->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'));
$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->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();
$result = $this->textDocument
->references(new ReferenceContext(), new TextDocumentIdentifier('global_symbols'), new Position(6, 9))
->wait();
$this->assertEquals([], $result);
}
@ -46,23 +53,25 @@ 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)
)->wait();
$this->assertEquals([new Location('global_fallback', new Range(new Position(6, 5), new Position(6, 15)))], $result);
$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
);
}
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);
$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
);
}
}

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Tests\Server\TextDocument\References;
@ -14,11 +14,13 @@ 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
)->wait();
$result = $this->textDocument
->references(
new ReferenceContext(),
new TextDocumentIdentifier($definition->uri),
$definition->range->start
)
->wait();
$this->assertEquals($this->getReferenceLocations('TestClass'), $result);
}
@ -27,11 +29,13 @@ 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
)->wait();
$result = $this->textDocument
->references(
new ReferenceContext(),
new TextDocumentIdentifier($definition->uri),
$definition->range->start
)
->wait();
$this->assertEquals($this->getReferenceLocations('TestClass::TEST_CLASS_CONST'), $result);
}
@ -40,11 +44,13 @@ 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
)->wait();
$result = $this->textDocument
->references(
new ReferenceContext(),
new TextDocumentIdentifier($definition->uri),
$definition->range->start
)
->wait();
$this->assertEquals($this->getReferenceLocations('TEST_CONST'), $result);
}
@ -53,11 +59,13 @@ 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
)->wait();
$result = $this->textDocument
->references(
new ReferenceContext(),
new TextDocumentIdentifier($definition->uri),
$definition->range->start
)
->wait();
$this->assertEquals($this->getReferenceLocations('TestClass::staticTestMethod()'), $result);
}
@ -66,11 +74,13 @@ 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
)->wait();
$result = $this->textDocument
->references(
new ReferenceContext(),
new TextDocumentIdentifier($definition->uri),
$definition->range->start
)
->wait();
$this->assertEquals($this->getReferenceLocations('TestClass::staticTestProperty'), $result);
}
@ -79,11 +89,13 @@ 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
)->wait();
$result = $this->textDocument
->references(
new ReferenceContext(),
new TextDocumentIdentifier($definition->uri),
$definition->range->start
)
->wait();
$this->assertEquals($this->getReferenceLocations('TestClass::testMethod()'), $result);
}
@ -92,11 +104,13 @@ 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
)->wait();
$result = $this->textDocument
->references(
new ReferenceContext(),
new TextDocumentIdentifier($definition->uri),
$definition->range->start
)
->wait();
$this->assertEquals($this->getReferenceLocations('TestClass::testProperty'), $result);
}
@ -105,16 +119,17 @@ 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)
)->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);
$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
);
}
public function testReferencesForFunctionParams()
@ -122,11 +137,9 @@ 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)
)->wait();
$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);
}
@ -135,16 +148,17 @@ class GlobalTest extends ServerTestCase
// 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);
$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
);
}
public function testReferencesForReference()
@ -152,11 +166,9 @@ class GlobalTest extends ServerTestCase
// $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();
$result = $this->textDocument
->references(new ReferenceContext(), new TextDocumentIdentifier($reference->uri), $reference->range->start)
->wait();
$this->assertEquals($this->getReferenceLocations('TestClass'), $result);
}
@ -165,11 +177,9 @@ class GlobalTest extends ServerTestCase
// 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();
$result = $this->textDocument
->references(new ReferenceContext(), new TextDocumentIdentifier($symbolsUri), new Position(111, 10))
->wait();
$this->assertEquals([], $result);
}
@ -178,11 +188,9 @@ class GlobalTest extends ServerTestCase
// 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();
$result = $this->textDocument
->references(new ReferenceContext(), new TextDocumentIdentifier($symbolsUri), new Position(113, 18))
->wait();
$this->assertEquals([], $result);
}
@ -191,11 +199,9 @@ class GlobalTest extends ServerTestCase
// 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();
$result = $this->textDocument
->references(new ReferenceContext(), new TextDocumentIdentifier($symbolsUri), new Position(115, 26))
->wait();
$this->assertEquals([], $result);
}
}

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Tests\Server\TextDocument\References;
@ -23,11 +23,9 @@ class NamespacedTest extends GlobalTest
// namespace TestNamespace;
// Get references for TestNamespace
$definition = parent::getDefinitionLocation('TestNamespace');
$result = $this->textDocument->references(
new ReferenceContext,
new TextDocumentIdentifier($definition->uri),
$definition->range->end
)->wait();
$result = $this->textDocument
->references(new ReferenceContext(), new TextDocumentIdentifier($definition->uri), $definition->range->end)
->wait();
$this->assertEquals(parent::getReferenceLocations('TestNamespace'), $result);
}
}

View File

@ -1,13 +1,11 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Tests\Server\TextDocument;
use PHPUnit\Framework\TestCase;
use LanguageServer\Tests\MockProtocolStream;
use LanguageServer\{
Server, LanguageClient, PhpDocumentLoader, DefinitionResolver
};
use LanguageServer\{Server, LanguageClient, PhpDocumentLoader, DefinitionResolver};
use LanguageServer\Index\{Index, ProjectIndex, DependenciesIndex};
use LanguageServer\ContentRetriever\FileSystemContentRetriever;
use LanguageServerProtocol\{
@ -35,11 +33,11 @@ class SignatureHelpTest extends TestCase
public function setUp()
{
$client = new LanguageClient(new MockProtocolStream, new MockProtocolStream);
$client = new LanguageClient(new MockProtocolStream(), new MockProtocolStream());
$index = new Index();
$projectIndex = new ProjectIndex($index, new DependenciesIndex);
$projectIndex = new ProjectIndex($index, new DependenciesIndex());
$definitionResolver = new DefinitionResolver($projectIndex);
$contentRetriever = new FileSystemContentRetriever;
$contentRetriever = new FileSystemContentRetriever();
$this->loader = new PhpDocumentLoader($contentRetriever, $projectIndex, $definitionResolver);
$this->textDocument = new Server\TextDocument($this->loader, $definitionResolver, $client, $projectIndex);
$index->setComplete();
@ -52,10 +50,7 @@ class SignatureHelpTest extends TestCase
{
$callsUri = pathToUri(__DIR__ . '/../../../fixtures/signature_help/calls.php');
$this->loader->open($callsUri, file_get_contents($callsUri));
$signatureHelp = $this->textDocument->signatureHelp(
new TextDocumentIdentifier($callsUri),
$position
)->wait();
$signatureHelp = $this->textDocument->signatureHelp(new TextDocumentIdentifier($callsUri), $position)->wait();
$this->assertEquals($expectedSignature, $signatureHelp);
}
@ -69,15 +64,18 @@ class SignatureHelpTest extends TestCase
new SignatureInformation(
'(\\Foo\\SomethingElse $a, int|null $b = null)',
[
new ParameterInformation('\\Foo\\SomethingElse $a', 'A param with a different doc type'),
new ParameterInformation('int|null $b = null', 'Param with default value'),
new ParameterInformation(
'\\Foo\\SomethingElse $a',
'A param with a different doc type'
),
new ParameterInformation('int|null $b = null', 'Param with default value')
],
'Function doc'
)
],
0,
0
),
)
],
'member call 2nd param active' => [
new Position(51, 12),
@ -86,15 +84,18 @@ class SignatureHelpTest extends TestCase
new SignatureInformation(
'(\\Foo\\SomethingElse $a, int|null $b = null)',
[
new ParameterInformation('\\Foo\\SomethingElse $a', 'A param with a different doc type'),
new ParameterInformation('int|null $b = null', 'Param with default value'),
new ParameterInformation(
'\\Foo\\SomethingElse $a',
'A param with a different doc type'
),
new ParameterInformation('int|null $b = null', 'Param with default value')
],
'Function doc'
)
],
0,
1
),
)
],
'member call 2nd param active and closing )' => [
new Position(52, 11),
@ -103,19 +104,22 @@ class SignatureHelpTest extends TestCase
new SignatureInformation(
'(\\Foo\\SomethingElse $a, int|null $b = null)',
[
new ParameterInformation('\\Foo\\SomethingElse $a', 'A param with a different doc type'),
new ParameterInformation('int|null $b = null', 'Param with default value'),
new ParameterInformation(
'\\Foo\\SomethingElse $a',
'A param with a different doc type'
),
new ParameterInformation('int|null $b = null', 'Param with default value')
],
'Function doc'
)
],
0,
1
),
)
],
'method with no params' => [
new Position(53, 9),
new SignatureHelp([new SignatureInformation('()', [], 'Method with no params', 0, 0)]),
new SignatureHelp([new SignatureInformation('()', [], 'Method with no params', 0, 0)])
],
'constructor' => [
new Position(48, 14),
@ -126,14 +130,14 @@ class SignatureHelpTest extends TestCase
[
new ParameterInformation('string $first', 'First param'),
new ParameterInformation('int $second', 'Second param'),
new ParameterInformation('\Foo\Test $third', 'Third param with a longer description'),
new ParameterInformation('\Foo\Test $third', 'Third param with a longer description')
],
'Constructor comment goes here'
)
],
0,
0
),
)
],
'constructor argument expression list' => [
new Position(49, 16),
@ -144,27 +148,24 @@ class SignatureHelpTest extends TestCase
[
new ParameterInformation('string $first', 'First param'),
new ParameterInformation('int $second', 'Second param'),
new ParameterInformation('\Foo\Test $third', 'Third param with a longer description'),
new ParameterInformation('\Foo\Test $third', 'Third param with a longer description')
],
'Constructor comment goes here'
)
],
0,
1
),
)
],
'global function' => [
new Position(57, 15),
new SignatureHelp(
[
new SignatureInformation(
'(int $i, bool $b = false, \Foo\Test|null ...$things = null)',
[
new ParameterInformation('int $i', 'Global function param one'),
new ParameterInformation('bool $b = false', 'Default false param'),
new ParameterInformation('\Foo\Test|null ...$things = null', 'Test things'),
]
),
new SignatureInformation('(int $i, bool $b = false, \Foo\Test|null ...$things = null)', [
new ParameterInformation('int $i', 'Global function param one'),
new ParameterInformation('bool $b = false', 'Default false param'),
new ParameterInformation('\Foo\Test|null ...$things = null', 'Test things')
])
],
0,
2
@ -176,24 +177,15 @@ class SignatureHelpTest extends TestCase
[new SignatureInformation('(mixed $a)', [new ParameterInformation('mixed $a')])],
0,
0
),
],
'no signature help' => [
new Position(0, 0),
new SignatureHelp([]),
],
'construct from non fqn (not supported)' => [
new Position(62, 9),
new SignatureHelp([]),
)
],
'no signature help' => [new Position(0, 0), new SignatureHelp([])],
'construct from non fqn (not supported)' => [new Position(62, 9), new SignatureHelp([])],
'construct from non fqn (not supported) argument expression' => [
new Position(63, 11),
new SignatureHelp([]),
],
'invalid var' => [
new Position(65, 13),
new SignatureHelp([]),
new SignatureHelp([])
],
'invalid var' => [new Position(65, 13), new SignatureHelp([])]
];
}
}

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Tests\Server\Workspace;
@ -17,11 +17,19 @@ class DidChangeWatchedFilesTest extends ServerTestCase
{
public function testDeletingFileClearsAllDiagnostics()
{
$client = new LanguageClient(new MockProtocolStream(), $writer = new MockProtocolStream());
$projectIndex = new ProjectIndex($sourceIndex = new Index(), $dependenciesIndex = new DependenciesIndex());
$client = new LanguageClient(new MockProtocolStream(), ($writer = new MockProtocolStream()));
$projectIndex = new ProjectIndex(($sourceIndex = new Index()), ($dependenciesIndex = new DependenciesIndex()));
$definitionResolver = new DefinitionResolver($projectIndex);
$loader = new PhpDocumentLoader(new FileSystemContentRetriever(), $projectIndex, $definitionResolver);
$workspace = new Server\Workspace($client, $projectIndex, $dependenciesIndex, $sourceIndex, null, $loader, null);
$workspace = new Server\Workspace(
$client,
$projectIndex,
$dependenciesIndex,
$sourceIndex,
null,
$loader,
null
);
$fileEvent = new FileEvent('my uri', FileChangeType::DELETED);

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Tests\Server\Workspace;
@ -29,46 +29,204 @@ class SymbolTest extends ServerTestCase
$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()'), ''),
$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);
new SymbolInformation(
'SecondTestNamespace',
SymbolKind::NAMESPACE,
$this->getDefinitionLocation('SecondTestNamespace'),
''
)
],
$result
);
// @codingStandardsIgnoreEnd
}
@ -77,12 +235,35 @@ class SymbolTest extends ServerTestCase
// 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);
$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
}
}

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Tests\Utils;

View File

@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Tests\Utils;

View File

@ -1,6 +1,6 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
namespace LanguageServer\Tests;
@ -31,7 +31,10 @@ class ValidationTest extends TestCase
$disabled = json_decode(file_get_contents(__DIR__ . '/disabled.json'));
foreach (new RecursiveIteratorIterator($iterator) as $file) {
if (strpos(\strrev((string)$file), \strrev(".php")) === 0 && !\in_array(basename((string)$file), $disabled)) {
if (
strpos(\strrev((string) $file), \strrev(".php")) === 0 &&
!\in_array(basename((string) $file), $disabled)
) {
if ($file->getSize() < 100000) {
$testProviderArray[] = [$file->getPathname()];
}
@ -57,17 +60,21 @@ class ValidationTest extends TestCase
$outputFile = getExpectedValuesFile($testCaseFile);
if (!file_exists($outputFile)) {
file_put_contents($outputFile, json_encode($actualValues, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
file_put_contents($outputFile, json_encode($actualValues, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
}
$expectedValues = (array)json_decode(file_get_contents($outputFile));
$expectedValues = (array) json_decode(file_get_contents($outputFile));
try {
$this->assertEquals($expectedValues['definitions'], $actualValues['definitions']);
$this->assertEquals((array)$expectedValues['references'], (array)$actualValues['references'], sprintf('references match in "%s"', $outputFile));
$this->assertEquals(
(array) $expectedValues['references'],
(array) $actualValues['references'],
sprintf('references match in "%s"', $outputFile)
);
} catch (\Throwable $e) {
$outputFile = getExpectedValuesFile($testCaseFile);
file_put_contents($outputFile, json_encode($actualValues, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
file_put_contents($outputFile, json_encode($actualValues, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
throw $e;
}
@ -106,10 +113,22 @@ class ValidationTest extends TestCase
// Turn def locations into relative paths
foreach ($refsAndDefs['definitions'] as $key => $def) {
if ($def !== null && $def->symbolInformation !== null &&
$def->symbolInformation->location !== null && $def->symbolInformation->location->uri !== null) {
$def->symbolInformation->location->uri = str_replace($testCasesDir, '.', $def->symbolInformation->location->uri);
$def->symbolInformation->location->uri = str_replace(DIRECTORY_SEPARATOR, '/', $def->symbolInformation->location->uri);
if (
$def !== null &&
$def->symbolInformation !== null &&
$def->symbolInformation->location !== null &&
$def->symbolInformation->location->uri !== null
) {
$def->symbolInformation->location->uri = str_replace(
$testCasesDir,
'.',
$def->symbolInformation->location->uri
);
$def->symbolInformation->location->uri = str_replace(
DIRECTORY_SEPARATOR,
'/',
$def->symbolInformation->location->uri
);
}
}
@ -135,7 +154,7 @@ class ValidationTest extends TestCase
} elseif ($propertyName === 'extends') {
$definition->$propertyName = $definition->$propertyName ?? [];
} elseif ($propertyName === 'type' && $definition->type !== null) {
$defsForAssert[$fqn]['type__tostring'] = (string)$definition->type;
$defsForAssert[$fqn]['type__tostring'] = (string) $definition->type;
}
$defsForAssert[$fqn][$propertyName] = $definition->$propertyName;

View File

@ -1,7 +1 @@
[
"forLoopReference1.php",
"namespaces3.php",
"parameterTypeResolution1.php",
"parent2.php",
"newStatic.php"
]
["forLoopReference1.php", "namespaces3.php", "parameterTypeResolution1.php", "parent2.php", "newStatic.php"]