From a454cd2873c0e137e538560e1bddada55386001f Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 19 Jun 2017 23:35:47 -0700 Subject: [PATCH 1/7] Add vendor/validation folders to search.exclude (#420) --- .gitignore | 1 - .vscode/settings.json | 7 +++++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 .vscode/settings.json diff --git a/.gitignore b/.gitignore index e7997bf..f6b089b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ .DS_Store -.vscode .idea vendor/ .phpls/ diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..38150bd --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +// Place your settings in this file to overwrite default and user settings. +{ + "search.exclude": { + "**/validation": true, + "**/tests/Validation/cases": true + } +} From 08fe84de35221d107d581654b07f84aed736cc3c Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Tue, 20 Jun 2017 08:38:06 +0200 Subject: [PATCH 2/7] Add launch.json --- .vscode/launch.json | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..0f77709 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,29 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Listen for XDebug", + "type": "php", + "request": "launch", + "port": 9000 + }, + { + "name": "Launch currently open script", + "type": "php", + "request": "launch", + "program": "${file}", + "cwd": "${fileDirname}", + "port": 9000 + }, + { + "name": "PHPUnit", + "type": "php", + "request": "launch", + "program": "${workspaceRoot}/vendor/phpunit/phpunit/phpunit", + "cwd": "${workspaceRoot}", + "args": [ + // "--filter", "CompletionTest" + ] + } + ] +} From f43ce50d5ab534581212e0abb74841d9fb18a90a Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Wed, 21 Jun 2017 11:48:41 +0200 Subject: [PATCH 3/7] Default memory limit to 4GB --- README.md | 2 +- bin/php-language-server.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 37800e0..432d874 100644 --- a/README.md +++ b/README.md @@ -170,7 +170,7 @@ Example: #### `--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. -By default there is no memory limit. +The default is 4GB (which is way more than needed). Example: diff --git a/bin/php-language-server.php b/bin/php-language-server.php index 0e3de2a..2a2ab69 100644 --- a/bin/php-language-server.php +++ b/bin/php-language-server.php @@ -6,7 +6,7 @@ use Composer\{Factory, XdebugHandler}; $options = getopt('', ['tcp::', 'tcp-server::', 'memory-limit::']); -ini_set('memory_limit', $options['memory-limit'] ?? -1); +ini_set('memory_limit', $options['memory-limit'] ?? '4G'); foreach ([__DIR__ . '/../../../autoload.php', __DIR__ . '/../autoload.php', __DIR__ . '/../vendor/autoload.php'] as $file) { if (file_exists($file)) { From 00552120ad6efadd9e7f2e167b363372c92dc6aa Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Wed, 21 Jun 2017 14:17:36 +0200 Subject: [PATCH 4/7] Restrict workspace/symbol results to non-dependency symbols (#426) This improves performance a lot and matches what other language servers do --- src/Server/Workspace.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Server/Workspace.php b/src/Server/Workspace.php index d2f8cec..e663a06 100644 --- a/src/Server/Workspace.php +++ b/src/Server/Workspace.php @@ -33,7 +33,7 @@ class Workspace * * @var ProjectIndex */ - private $index; + private $projectIndex; /** * @var DependenciesIndex @@ -57,17 +57,17 @@ class Workspace /** * @param LanguageClient $client LanguageClient instance used to signal updated results - * @param ProjectIndex $index Index that is searched on a workspace/symbol request + * @param ProjectIndex $projectIndex Index that is used to wait for full index completeness * @param DependenciesIndex $dependenciesIndex Index that is used on a workspace/xreferences request * @param DependenciesIndex $sourceIndex Index that is used on a workspace/xreferences request * @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 $index, 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->index = $index; + $this->projectIndex = $projectIndex; $this->dependenciesIndex = $dependenciesIndex; $this->composerLock = $composerLock; $this->documentLoader = $documentLoader; @@ -84,11 +84,11 @@ class Workspace { return coroutine(function () use ($query) { // Wait until indexing for definitions finished - if (!$this->index->isStaticComplete()) { - yield waitForEvent($this->index, 'static-complete'); + if (!$this->sourceIndex->isStaticComplete()) { + yield waitForEvent($this->sourceIndex, 'static-complete'); } $symbols = []; - foreach ($this->index->getDefinitions() as $fqn => $definition) { + foreach ($this->sourceIndex->getDefinitions() as $fqn => $definition) { if ($query === '' || stripos($fqn, $query) !== false) { $symbols[] = $definition->symbolInformation; } @@ -126,8 +126,8 @@ class Workspace return []; } // Wait until indexing finished - if (!$this->index->isComplete()) { - yield waitForEvent($this->index, 'complete'); + if (!$this->projectIndex->isComplete()) { + yield waitForEvent($this->projectIndex, 'complete'); } /** Map from URI to array of referenced FQNs in dependencies */ $refs = []; From fced1d5af62db1d2c2c3d7b16d2ee055fb3c64b7 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Thu, 22 Jun 2017 17:34:28 +0200 Subject: [PATCH 5/7] Fix textDocument/xdefinition (#429) --- src/Server/TextDocument.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Server/TextDocument.php b/src/Server/TextDocument.php index 2e8e4bf..be34dc2 100644 --- a/src/Server/TextDocument.php +++ b/src/Server/TextDocument.php @@ -368,9 +368,10 @@ class TextDocument return []; } // Handle definition nodes + $fqn = DefinitionResolver::getDefinedFqn($node); while (true) { if ($fqn) { - $def = $this->index->getDefinition($definedFqn); + $def = $this->index->getDefinition($fqn); } else { // Handle reference nodes $def = $this->definitionResolver->resolveReferenceNodeToDefinition($node); From fc0bf4c163e94d39ff14c9209050b67bbda2bf30 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Thu, 22 Jun 2017 20:06:10 +0200 Subject: [PATCH 6/7] Fix workspace/xreferences (#424) * Make Descriptors minimal SymbolDescriptor and PackageDescriptor should only contain the minumum amount of properties needed * Add missing use * Fixes * Ignore ReferenceInformation->symbol --- src/Protocol/PackageDescriptor.php | 25 +++++++++++++++++++ src/Protocol/SymbolDescriptor.php | 17 +++++++------ src/Server/TextDocument.php | 39 +++++++++++++++--------------- src/Server/Workspace.php | 35 ++++----------------------- 4 files changed, 59 insertions(+), 57 deletions(-) create mode 100644 src/Protocol/PackageDescriptor.php diff --git a/src/Protocol/PackageDescriptor.php b/src/Protocol/PackageDescriptor.php new file mode 100644 index 0000000..e635740 --- /dev/null +++ b/src/Protocol/PackageDescriptor.php @@ -0,0 +1,25 @@ +name = $name; + } +} diff --git a/src/Protocol/SymbolDescriptor.php b/src/Protocol/SymbolDescriptor.php index fa74bcb..4116864 100644 --- a/src/Protocol/SymbolDescriptor.php +++ b/src/Protocol/SymbolDescriptor.php @@ -3,7 +3,10 @@ declare(strict_types = 1); namespace LanguageServer\Protocol; -class SymbolDescriptor extends SymbolInformation +/** + * Uniquely identifies a symbol + */ +class SymbolDescriptor { /** * The fully qualified structural element name, a globally unique identifier for the symbol. @@ -13,19 +16,17 @@ class SymbolDescriptor extends SymbolInformation public $fqsen; /** - * A package from the composer.lock file or the contents of the composer.json - * Example: https://github.com/composer/composer/blob/master/composer.lock#L10 - * Available fields may differ + * Identifies the Composer package the symbol is defined in (if any) * - * @var object|null + * @var PackageDescriptor|null */ public $package; /** - * @param string $fqsen The fully qualified structural element name, a globally unique identifier for the symbol. - * @param object $package A package from the composer.lock file or the contents of the composer.json + * @param string $fqsen The fully qualified structural element name, a globally unique identifier for the symbol. + * @param PackageDescriptor $package Identifies the Composer package the symbol is defined in */ - public function __construct(string $fqsen = null, $package = null) + public function __construct(string $fqsen = null, PackageDescriptor $package = null) { $this->fqsen = $fqsen; $this->package = $package; diff --git a/src/Server/TextDocument.php b/src/Server/TextDocument.php index be34dc2..5a2819e 100644 --- a/src/Server/TextDocument.php +++ b/src/Server/TextDocument.php @@ -8,14 +8,26 @@ use LanguageServer\{ }; use LanguageServer\Index\ReadableIndex; use LanguageServer\Protocol\{ - FormattingOptions, Hover, Location, MarkedString, Position, Range, ReferenceContext, SymbolDescriptor, SymbolLocationInformation, TextDocumentIdentifier, TextDocumentItem, VersionedTextDocumentIdentifier + FormattingOptions, + Hover, + Location, + MarkedString, + Position, + Range, + ReferenceContext, + SymbolDescriptor, + PackageDescriptor, + SymbolLocationInformation, + TextDocumentIdentifier, + TextDocumentItem, + VersionedTextDocumentIdentifier }; use Microsoft\PhpParser; use Microsoft\PhpParser\Node; use Sabre\Event\Promise; use Sabre\Uri; use function LanguageServer\{ - isVendored, waitForEvent + isVendored, waitForEvent, getPackageName }; use function Sabre\Event\coroutine; @@ -389,25 +401,14 @@ class TextDocument ) { return []; } - $symbol = new SymbolDescriptor; - foreach (get_object_vars($def->symbolInformation) as $prop => $val) { - $symbol->$prop = $val; - } - $symbol->fqsen = $def->fqn; + // if Definition is inside a dependency, use the package name $packageName = getPackageName($def->symbolInformation->location->uri, $this->composerJson); - if ($packageName && $this->composerLock !== null) { - // Definition is inside a dependency - foreach (array_merge($this->composerLock->packages, $this->composerLock->{'packages-dev'}) as $package) { - if ($package->name === $packageName) { - $symbol->package = $package; - break; - } - } - } else if ($this->composerJson !== null) { - // Definition belongs to a root package - $symbol->package = $this->composerJson; + // else use the package name of the root package (if exists) + if (!$packageName && $this->composerJson !== null) { + $packageName = $this->composerJson->name; } - return [new SymbolLocationInformation($symbol, $symbol->location)]; + $descriptor = new SymbolDescriptor($def->fqn, new PackageDescriptor($packageName)); + return [new SymbolLocationInformation($descriptor, $def->symbolInformation->location)]; }); } } diff --git a/src/Server/Workspace.php b/src/Server/Workspace.php index e663a06..61db068 100644 --- a/src/Server/Workspace.php +++ b/src/Server/Workspace.php @@ -10,13 +10,15 @@ use LanguageServer\Protocol\{ FileEvent, SymbolInformation, SymbolDescriptor, + PackageDescriptor, ReferenceInformation, DependencyReference, - Location + Location, + MessageType }; use Sabre\Event\Promise; use function Sabre\Event\coroutine; -use function LanguageServer\{waitForEvent, getPackageName}; +use function LanguageServer\waitForEvent; /** * Provides method handlers for all workspace/* methods @@ -146,38 +148,11 @@ class Workspace $refInfos = []; foreach ($refs as $uri => $fqns) { foreach ($fqns as $fqn) { - $def = $this->dependenciesIndex->getDefinition($fqn); - $symbol = new SymbolDescriptor; - $symbol->fqsen = $fqn; - foreach (get_object_vars($def->symbolInformation) as $prop => $val) { - $symbol->$prop = $val; - } - // Find out package name - $packageName = getPackageName($def->symbolInformation->location->uri, $this->composerJson); - foreach (array_merge($this->composerLock->packages, $this->composerLock->{'packages-dev'}) as $package) { - if ($package->name === $packageName) { - $symbol->package = $package; - break; - } - } - // If there was no FQSEN provided, check if query attributes match - if (!isset($query->fqsen)) { - $matches = true; - foreach (get_object_vars($query) as $prop => $val) { - if ($query->$prop != $symbol->$prop) { - $matches = false; - break; - } - } - if (!$matches) { - continue; - } - } $doc = yield $this->documentLoader->getOrLoad($uri); foreach ($doc->getReferenceNodesByFqn($fqn) as $node) { $refInfo = new ReferenceInformation; $refInfo->reference = Location::fromNode($node); - $refInfo->symbol = $symbol; + $refInfo->symbol = $query; $refInfos[] = $refInfo; } } From 94fc0405fd4aed4fe7d779dc04a2924e3ced5b45 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sat, 1 Jul 2017 14:32:56 +0200 Subject: [PATCH 7/7] Correct parser link in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 432d874..ef351ec 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ A pure PHP implementation of the open [Language Server Protocol](https://github.com/Microsoft/language-server-protocol). Provides static code analysis for PHP for any IDE. -Uses the great [PHP-Parser](https://github.com/nikic/PHP-Parser), +Uses the great [Tolerant PHP Parser](https://github.com/Microsoft/tolerant-php-parser), [phpDocumentor's DocBlock reflection](https://github.com/phpDocumentor/ReflectionDocBlock) and an [event loop](http://sabre.io/event/loop/) for concurrency.