From ddf8274879ddeff908b87a5a5678700298269c7f Mon Sep 17 00:00:00 2001 From: Ivan Bozhanov Date: Thu, 19 Jan 2017 16:35:12 +0200 Subject: [PATCH 01/22] handle hovering $this --- src/DefinitionResolver.php | 4 ++++ tests/Server/TextDocument/HoverTest.php | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index d1f756e..8cbddf7 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -144,6 +144,10 @@ class DefinitionResolver { // Variables are not indexed globally, as they stay in the file scope anyway if ($node instanceof Node\Expr\Variable) { + // Resolve $this + if ($node->name === 'this' && $fqn = $this->getContainingClassFqn($node)) { + return $this->index->getDefinition($fqn, false); + } // Resolve the variable to a definition node (assignment, param or closure use) $defNode = self::resolveVariableToNode($node); if ($defNode === null) { diff --git a/tests/Server/TextDocument/HoverTest.php b/tests/Server/TextDocument/HoverTest.php index 9f33268..7d61354 100644 --- a/tests/Server/TextDocument/HoverTest.php +++ b/tests/Server/TextDocument/HoverTest.php @@ -172,4 +172,16 @@ class HoverTest extends ServerTestCase new Range(new Position(22, 9), new Position(22, 15)) ), $result); } + + public function testHoverForThis() + { + // $this; + // 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', " Date: Thu, 19 Jan 2017 20:33:31 +0200 Subject: [PATCH 02/22] added signature help --- fixtures/global_symbols.php | 6 ++- src/Definition.php | 7 +++ src/DefinitionResolver.php | 6 +++ src/LanguageServer.php | 3 ++ src/Server/TextDocument.php | 16 +++++- src/SignatureHelpProvider.php | 91 +++++++++++++++++++++++++++++++++++ 6 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 src/SignatureHelpProvider.php diff --git a/fixtures/global_symbols.php b/fixtures/global_symbols.php index 6494848..787b1c7 100644 --- a/fixtures/global_symbols.php +++ b/fixtures/global_symbols.php @@ -55,9 +55,13 @@ class TestClass implements TestInterface * @param TestClass $testParameter Lorem sunt velit incididunt mollit * @return TestClass */ - public function testMethod($testParameter): TestInterface + public function testMethod( + string $testParameter = '()', + string $asdf = "," + ) : TestInterface { $this->testProperty = $testParameter; + $this->testMethod(); } } diff --git a/src/Definition.php b/src/Definition.php index d4b59cb..dab8295 100644 --- a/src/Definition.php +++ b/src/Definition.php @@ -89,4 +89,11 @@ class Definition * @var string */ public $documentation; + + /** + * Parameters array (for methods and functions), for use in textDocument/signatureHelp + * + * @var string[] + */ + public $parameters; } diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index 8cbddf7..58bc9e9 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -131,6 +131,12 @@ class DefinitionResolver $def->type = $this->getTypeFromNode($node); $def->declarationLine = $this->getDeclarationLineFromNode($node); $def->documentation = $this->getDocumentationFromNode($node); + $def->parameters = []; + if ($node instanceof Node\FunctionLike) { + foreach ($node->getParams() as $param) { + $def->parameters[] = $this->prettyPrinter->prettyPrint([$param]); + } + } return $def; } diff --git a/src/LanguageServer.php b/src/LanguageServer.php index b0af2f0..b4ba22b 100644 --- a/src/LanguageServer.php +++ b/src/LanguageServer.php @@ -247,6 +247,9 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher $serverCapabilities->completionProvider = new CompletionOptions; $serverCapabilities->completionProvider->resolveProvider = false; $serverCapabilities->completionProvider->triggerCharacters = ['$', '>']; + // Support "Signature Help" + $serverCapabilities->signatureHelpProvider = new SignatureHelpOptions; + $serverCapabilities->signatureHelpProvider->triggerCharacters = ['(']; // Support global references $serverCapabilities->xworkspaceReferencesProvider = true; $serverCapabilities->xdefinitionProvider = true; diff --git a/src/Server/TextDocument.php b/src/Server/TextDocument.php index a3183dc..b956698 100644 --- a/src/Server/TextDocument.php +++ b/src/Server/TextDocument.php @@ -5,7 +5,7 @@ namespace LanguageServer\Server; use PhpParser\PrettyPrinter\Standard as PrettyPrinter; use PhpParser\{Node, NodeTraverser}; -use LanguageServer\{LanguageClient, PhpDocumentLoader, PhpDocument, DefinitionResolver, CompletionProvider}; +use LanguageServer\{LanguageClient, PhpDocumentLoader, PhpDocument, DefinitionResolver, CompletionProvider, SignatureHelpProvider}; use LanguageServer\NodeVisitor\VariableReferencesCollector; use LanguageServer\Protocol\{ SymbolLocationInformation, @@ -63,6 +63,11 @@ class TextDocument */ protected $completionProvider; + /** + * @var SignatureHelpProvider + */ + protected $signatureHelpProvider; + /** * @var ReadableIndex */ @@ -99,6 +104,7 @@ class TextDocument $this->prettyPrinter = new PrettyPrinter(); $this->definitionResolver = $definitionResolver; $this->completionProvider = new CompletionProvider($this->definitionResolver, $index); + $this->signatureHelpProvider = new SignatureHelpProvider($this->definitionResolver, $index); $this->index = $index; $this->composerJson = $composerJson; $this->composerLock = $composerLock; @@ -343,6 +349,14 @@ class TextDocument }); } + public function signatureHelp(TextDocumentIdentifier $textDocument, Position $position): Promise + { + return coroutine(function () use ($textDocument, $position) { + $document = yield $this->documentLoader->getOrLoad($textDocument->uri); + return $this->signatureHelpProvider->provideSignature($document, $position); + }); + } + /** * This method is the same as textDocument/definition, except that * diff --git a/src/SignatureHelpProvider.php b/src/SignatureHelpProvider.php new file mode 100644 index 0000000..553e0bf --- /dev/null +++ b/src/SignatureHelpProvider.php @@ -0,0 +1,91 @@ +definitionResolver = $definitionResolver; + $this->index = $index; + } + + /** + * Returns signature help for a specific cursor position in a document + * + * @param PhpDocument $doc The opened document + * @param Position $pos The cursor position + * @return SignatureHelp + */ + public function provideSignature(PhpDocument $doc, Position $pos): SignatureHelp + { + $node = $doc->getNodeAtPosition($pos); + $help = new SignatureHelp; + $help->signatures = []; + + if ($node instanceof Node\Expr\FuncCall) { + if ($def = $this->definitionResolver->resolveReferenceNodeToDefinition($node)) { + $signature = new SignatureInformation; + $signature->label = str_replace('()', '', $def->fqn); + $signature->documentation = $def->documentation; + $signature->parameters = []; + foreach ($def->parameters as $param) { + $p = new ParameterInformation; + $p->label = $param; + $signature->parameters[] = $p; + } + $help->signatures[] = $signature; + } + } else if ($node instanceof Node\Expr\MethodCall) { + if ($def = $this->definitionResolver->resolveReferenceNodeToDefinition($node)) { + $signature = new SignatureInformation; + $signature->label = str_replace('()', '', explode('->', $def->fqn)[1]); + $signature->documentation = $def->documentation; + $signature->parameters = []; + foreach ($def->parameters as $param) { + $p = new ParameterInformation; + $p->label = $param; + $signature->parameters[] = $p; + } + $help->signatures[] = $signature; + } + } else if ($node instanceof Node\Expr\StaticCall) { + $signature = new SignatureInformation; + $signature->label = str_replace('()', '', explode('::', $def->fqn)[1]); + $signature->documentation = $def->documentation; + $signature->parameters = []; + foreach ($def->parameters as $param) { + $p = new ParameterInformation; + $p->label = $param; + $signature->parameters[] = $p; + } + $help->signatures[] = $signature; + } + + return $help; + } +} From 6bca8df7585ed4d078b8a626893c0249c4dc2377 Mon Sep 17 00:00:00 2001 From: Ivan Bozhanov Date: Thu, 19 Jan 2017 20:35:20 +0200 Subject: [PATCH 03/22] restored fixture --- fixtures/global_symbols.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/fixtures/global_symbols.php b/fixtures/global_symbols.php index 787b1c7..6494848 100644 --- a/fixtures/global_symbols.php +++ b/fixtures/global_symbols.php @@ -55,13 +55,9 @@ class TestClass implements TestInterface * @param TestClass $testParameter Lorem sunt velit incididunt mollit * @return TestClass */ - public function testMethod( - string $testParameter = '()', - string $asdf = "," - ) : TestInterface + public function testMethod($testParameter): TestInterface { $this->testProperty = $testParameter; - $this->testMethod(); } } From 67be71a847acc5f61e17b393684e0116ea76319d Mon Sep 17 00:00:00 2001 From: Ivan Bozhanov Date: Thu, 19 Jan 2017 20:37:10 +0200 Subject: [PATCH 04/22] added missing class --- src/LanguageServer.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/LanguageServer.php b/src/LanguageServer.php index b4ba22b..bb97d69 100644 --- a/src/LanguageServer.php +++ b/src/LanguageServer.php @@ -12,7 +12,8 @@ use LanguageServer\Protocol\{ InitializeResult, SymbolInformation, TextDocumentIdentifier, - CompletionOptions + CompletionOptions, + SignatureHelpOptions }; use LanguageServer\FilesFinder\{FilesFinder, ClientFilesFinder, FileSystemFilesFinder}; use LanguageServer\ContentRetriever\{ContentRetriever, ClientContentRetriever, FileSystemContentRetriever}; From 1e109a32d6637d7e4a7b8815c69d550e612095ec Mon Sep 17 00:00:00 2001 From: Ivan Bozhanov Date: Thu, 19 Jan 2017 22:18:14 +0200 Subject: [PATCH 05/22] added tests --- fixtures/signatureHelp/funcClosed.php | 6 + fixtures/signatureHelp/funcNotClosed.php | 6 + fixtures/signatureHelp/methodClosed.php | 12 ++ fixtures/signatureHelp/methodNotClosed.php | 12 ++ fixtures/signatureHelp/staticClosed.php | 10 + fixtures/signatureHelp/staticNotClosed.php | 10 + src/SignatureHelpProvider.php | 112 +++++++++-- tests/LanguageServerTest.php | 9 +- .../Server/TextDocument/SignatureHelpTest.php | 178 ++++++++++++++++++ 9 files changed, 341 insertions(+), 14 deletions(-) create mode 100644 fixtures/signatureHelp/funcClosed.php create mode 100644 fixtures/signatureHelp/funcNotClosed.php create mode 100644 fixtures/signatureHelp/methodClosed.php create mode 100644 fixtures/signatureHelp/methodNotClosed.php create mode 100644 fixtures/signatureHelp/staticClosed.php create mode 100644 fixtures/signatureHelp/staticNotClosed.php create mode 100644 tests/Server/TextDocument/SignatureHelpTest.php diff --git a/fixtures/signatureHelp/funcClosed.php b/fixtures/signatureHelp/funcClosed.php new file mode 100644 index 0000000..3efbd9a --- /dev/null +++ b/fixtures/signatureHelp/funcClosed.php @@ -0,0 +1,6 @@ +method(); + } +} diff --git a/fixtures/signatureHelp/methodNotClosed.php b/fixtures/signatureHelp/methodNotClosed.php new file mode 100644 index 0000000..5c1fa9c --- /dev/null +++ b/fixtures/signatureHelp/methodNotClosed.php @@ -0,0 +1,12 @@ +method( + } +} diff --git a/fixtures/signatureHelp/staticClosed.php b/fixtures/signatureHelp/staticClosed.php new file mode 100644 index 0000000..301009f --- /dev/null +++ b/fixtures/signatureHelp/staticClosed.php @@ -0,0 +1,10 @@ +getNodeAtPosition($pos); $help = new SignatureHelp; $help->signatures = []; + $newPos = clone $pos; + $line = explode("\n", $doc->getContent())[$newPos->line]; + do { + $newPos->character --; + } while ($newPos->character > 0 && $line[$newPos->character] !== "("); + + if (!$newPos->character) { + return $help; + } + $line = substr($line, 0, $newPos->character); + + //echo $line . "\n"; + //die(); + $newPos->character --; + + $node = $doc->getNodeAtPosition($newPos); + + if ($node instanceof Node\Expr\Error) { + $node = $node->getAttribute('parentNode'); + } + + //echo get_class($node); + //die(); + //$def = $this->definitionResolver->resolveReferenceNodeToDefinition($node->var); + //var_dump($def); + //echo $def->fqn; + + //echo $node->name; + + + //die(); + + if ($node instanceof Node\Expr\Error) { + $node = $node->getAttribute('parentNode'); + } if ($node instanceof Node\Expr\FuncCall) { if ($def = $this->definitionResolver->resolveReferenceNodeToDefinition($node)) { $signature = new SignatureInformation; @@ -60,6 +94,22 @@ class SignatureHelpProvider } $help->signatures[] = $signature; } + } else if ($node instanceof Node\Name\FullyQualified || $node === null) { + if (preg_match('([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$)', $line, $method)) { + $fqn = $method[0] . '()'; + if ($def = $this->index->getDefinition($fqn)) { + $signature = new SignatureInformation; + $signature->label = $method[0]; + $signature->documentation = $def->documentation; + $signature->parameters = []; + foreach ($def->parameters as $param) { + $p = new ParameterInformation; + $p->label = $param; + $signature->parameters[] = $p; + } + $help->signatures[] = $signature; + } + } } else if ($node instanceof Node\Expr\MethodCall) { if ($def = $this->definitionResolver->resolveReferenceNodeToDefinition($node)) { $signature = new SignatureInformation; @@ -73,17 +123,57 @@ class SignatureHelpProvider } $help->signatures[] = $signature; } - } else if ($node instanceof Node\Expr\StaticCall) { - $signature = new SignatureInformation; - $signature->label = str_replace('()', '', explode('::', $def->fqn)[1]); - $signature->documentation = $def->documentation; - $signature->parameters = []; - foreach ($def->parameters as $param) { - $p = new ParameterInformation; - $p->label = $param; - $signature->parameters[] = $p; + } else if ($node instanceof Node\Expr\PropertyFetch) { + if ($def = $this->definitionResolver->resolveReferenceNodeToDefinition($node->var)) { + $method = trim(substr($line, strrpos($line, ">") + 1)); + if ($method) { + $fqn = $def->fqn . '->' . $method . '()'; + if ($def = $this->index->getDefinition($fqn)) { + $signature = new SignatureInformation; + $signature->label = str_replace('()', '', explode('->', $def->fqn)[1]); + $signature->documentation = $def->documentation; + $signature->parameters = []; + foreach ($def->parameters as $param) { + $p = new ParameterInformation; + $p->label = $param; + $signature->parameters[] = $p; + } + $help->signatures[] = $signature; + } + } + } + } else if ($node instanceof Node\Expr\StaticCall) { + if ($def = $this->definitionResolver->resolveReferenceNodeToDefinition($node)) { + $signature = new SignatureInformation; + $signature->label = str_replace('()', '', explode('::', $def->fqn)[1]); + $signature->documentation = $def->documentation; + $signature->parameters = []; + foreach ($def->parameters as $param) { + $p = new ParameterInformation; + $p->label = $param; + $signature->parameters[] = $p; + } + $help->signatures[] = $signature; + } + } else if ($node instanceof Node\Expr\ClassConstFetch) { + if ($def = $this->definitionResolver->resolveReferenceNodeToDefinition($node->class)) { + $method = trim(substr($line, strrpos($line, ":") + 1)); + if ($method) { + $fqn = $def->fqn . '::' . $method . '()'; + if ($def = $this->index->getDefinition($fqn)) { + $signature = new SignatureInformation; + $signature->label = str_replace('()', '', explode('::', $def->fqn)[1]); + $signature->documentation = $def->documentation; + $signature->parameters = []; + foreach ($def->parameters as $param) { + $p = new ParameterInformation; + $p->label = $param; + $signature->parameters[] = $p; + } + $help->signatures[] = $signature; + } + } } - $help->signatures[] = $signature; } return $help; diff --git a/tests/LanguageServerTest.php b/tests/LanguageServerTest.php index 6bfa3c9..7b1c008 100644 --- a/tests/LanguageServerTest.php +++ b/tests/LanguageServerTest.php @@ -14,7 +14,8 @@ use LanguageServer\Protocol\{ TextDocumentIdentifier, InitializeResult, ServerCapabilities, - CompletionOptions + CompletionOptions, + SignatureHelpOptions }; use AdvancedJsonRpc; use Webmozart\Glob\Glob; @@ -41,6 +42,8 @@ class LanguageServerTest extends TestCase $serverCapabilities->completionProvider = new CompletionOptions; $serverCapabilities->completionProvider->resolveProvider = false; $serverCapabilities->completionProvider->triggerCharacters = ['$', '>']; + $serverCapabilities->signatureHelpProvider = new SignatureHelpOptions; + $serverCapabilities->signatureHelpProvider->triggerCharacters = ['(']; $serverCapabilities->xworkspaceReferencesProvider = true; $serverCapabilities->xdefinitionProvider = true; $serverCapabilities->xdependenciesProvider = true; @@ -57,7 +60,7 @@ class LanguageServerTest extends TestCase 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 (strpos($msg->body->params->message, 'All 25 PHP files parsed') !== false) { + } else if (strpos($msg->body->params->message, 'All 33 PHP files parsed') !== false) { $promise->fulfill(); } } @@ -103,7 +106,7 @@ class LanguageServerTest extends TestCase if ($promise->state === Promise::PENDING) { $promise->reject(new Exception($msg->body->params->message)); } - } else if (strpos($msg->body->params->message, 'All 25 PHP files parsed') !== false) { + } else if (strpos($msg->body->params->message, 'All 33 PHP files parsed') !== false) { if ($run === 1) { $run++; } else { diff --git a/tests/Server/TextDocument/SignatureHelpTest.php b/tests/Server/TextDocument/SignatureHelpTest.php new file mode 100644 index 0000000..bc9c46f --- /dev/null +++ b/tests/Server/TextDocument/SignatureHelpTest.php @@ -0,0 +1,178 @@ +loader = new PhpDocumentLoader($contentRetriever, $projectIndex, $definitionResolver); + $this->loader->load(pathToUri(__DIR__ . '/../../../fixtures/global_symbols.php'))->wait(); + $this->loader->load(pathToUri(__DIR__ . '/../../../fixtures/symbols.php'))->wait(); + $this->textDocument = new Server\TextDocument($this->loader, $definitionResolver, $client, $projectIndex); + } + + public function testMethodClosed() + { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/signatureHelp/methodClosed.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $result = $this->textDocument->signatureHelp( + new TextDocumentIdentifier($completionUri), + new Position(9, 22) + )->wait(); + + $help = new SignatureHelp; + $help->signatures = []; + $info = new SignatureInformation; + $help->signatures[] = $info; + $info->label = 'method'; + $info->parameters = []; + $param = new ParameterInformation; + $info->parameters[] = $param; + $param->label = 'string $param = ""'; + + $this->assertEquals($help, $result); + } + + public function testMethodNotClosed() + { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/signatureHelp/methodNotClosed.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $result = $this->textDocument->signatureHelp( + new TextDocumentIdentifier($completionUri), + new Position(9, 22) + )->wait(); + + $help = new SignatureHelp; + $help->signatures = []; + $info = new SignatureInformation; + $help->signatures[] = $info; + $info->label = 'method'; + $info->parameters = []; + $param = new ParameterInformation; + $info->parameters[] = $param; + $param->label = 'string $param = ""'; + + $this->assertEquals($help, $result); + } + + public function funcClosed() + { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/signatureHelp/funcClosed.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $result = $this->textDocument->signatureHelp( + new TextDocumentIdentifier($completionUri), + new Position(5, 10) + )->wait(); + + $help = new SignatureHelp; + $help->signatures = []; + $info = new SignatureInformation; + $help->signatures[] = $info; + $info->label = 'helpFunc1'; + $info->parameters = []; + $param = new ParameterInformation; + $info->parameters[] = $param; + $param->label = 'int $count = 0'; + + $this->assertEquals($help, $result); + } + + public function funcNotClosed() + { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/signatureHelp/funcNotClosed.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $result = $this->textDocument->signatureHelp( + new TextDocumentIdentifier($completionUri), + new Position(5, 10) + )->wait(); + + $help = new SignatureHelp; + $help->signatures = []; + $info = new SignatureInformation; + $help->signatures[] = $info; + $info->label = 'helpFunc2'; + $info->parameters = []; + $param = new ParameterInformation; + $info->parameters[] = $param; + $param->label = 'int $count = 0'; + + $this->assertEquals($help, $result); + } + + public function staticClosed() + { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/signatureHelp/staticClosed.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $result = $this->textDocument->signatureHelp( + new TextDocumentIdentifier($completionUri), + new Position(9, 19) + )->wait(); + + $help = new SignatureHelp; + $help->signatures = []; + $info = new SignatureInformation; + $help->signatures[] = $info; + $info->label = 'method'; + $info->parameters = []; + $param = new ParameterInformation; + $info->parameters[] = $param; + $param->label = 'string $param = ""'; + + $this->assertEquals($help, $result); + } + + public function staticNotClosed() + { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/signatureHelp/staticNotClosed.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $result = $this->textDocument->signatureHelp( + new TextDocumentIdentifier($completionUri), + new Position(9, 19) + )->wait(); + + $help = new SignatureHelp; + $help->signatures = []; + $info = new SignatureInformation; + $help->signatures[] = $info; + $info->label = 'method'; + $info->parameters = []; + $param = new ParameterInformation; + $info->parameters[] = $param; + $param->label = 'string $param = ""'; + + $this->assertEquals($help, $result); + } +} From fdda97c7b51d73778d392e457b7f60cb082aa80f Mon Sep 17 00:00:00 2001 From: Ivan Bozhanov Date: Thu, 19 Jan 2017 22:20:21 +0200 Subject: [PATCH 06/22] fixed file count --- tests/LanguageServerTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/LanguageServerTest.php b/tests/LanguageServerTest.php index 7b1c008..a32c845 100644 --- a/tests/LanguageServerTest.php +++ b/tests/LanguageServerTest.php @@ -60,7 +60,7 @@ class LanguageServerTest extends TestCase 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 (strpos($msg->body->params->message, 'All 33 PHP files parsed') !== false) { + } else if (strpos($msg->body->params->message, 'All 31 PHP files parsed') !== false) { $promise->fulfill(); } } @@ -106,7 +106,7 @@ class LanguageServerTest extends TestCase if ($promise->state === Promise::PENDING) { $promise->reject(new Exception($msg->body->params->message)); } - } else if (strpos($msg->body->params->message, 'All 33 PHP files parsed') !== false) { + } else if (strpos($msg->body->params->message, 'All 31 PHP files parsed') !== false) { if ($run === 1) { $run++; } else { From 16c7560993d84c62cc2bf932246a7d23f34e8424 Mon Sep 17 00:00:00 2001 From: Ivan Bozhanov Date: Thu, 19 Jan 2017 23:54:05 +0200 Subject: [PATCH 07/22] fixed signature helper reference --- fixtures/signatureHelp/methodClosed.php | 5 +- fixtures/signatureHelp/methodNotClosed.php | 3 + src/SignatureHelpProvider.php | 38 ++++++------- .../Server/TextDocument/SignatureHelpTest.php | 56 +++++++++++++++++-- 4 files changed, 74 insertions(+), 28 deletions(-) diff --git a/fixtures/signatureHelp/methodClosed.php b/fixtures/signatureHelp/methodClosed.php index 24db705..a7430a5 100644 --- a/fixtures/signatureHelp/methodClosed.php +++ b/fixtures/signatureHelp/methodClosed.php @@ -2,7 +2,7 @@ class HelpClass1 { - protected function method(string $param = "") + public function method(string $param = "") { } public function test() @@ -10,3 +10,6 @@ class HelpClass1 $this->method(); } } + +$a = new HelpClass1; +$a->method(1, ); \ No newline at end of file diff --git a/fixtures/signatureHelp/methodNotClosed.php b/fixtures/signatureHelp/methodNotClosed.php index 5c1fa9c..af55912 100644 --- a/fixtures/signatureHelp/methodNotClosed.php +++ b/fixtures/signatureHelp/methodNotClosed.php @@ -10,3 +10,6 @@ class HelpClass2 $this->method( } } + +$a = new HelpClass2; +$a->method(1, \ No newline at end of file diff --git a/src/SignatureHelpProvider.php b/src/SignatureHelpProvider.php index 8f2d185..fe850d9 100644 --- a/src/SignatureHelpProvider.php +++ b/src/SignatureHelpProvider.php @@ -57,8 +57,6 @@ class SignatureHelpProvider } $line = substr($line, 0, $newPos->character); - //echo $line . "\n"; - //die(); $newPos->character --; $node = $doc->getNodeAtPosition($newPos); @@ -66,17 +64,6 @@ class SignatureHelpProvider if ($node instanceof Node\Expr\Error) { $node = $node->getAttribute('parentNode'); } - - //echo get_class($node); - //die(); - //$def = $this->definitionResolver->resolveReferenceNodeToDefinition($node->var); - //var_dump($def); - //echo $def->fqn; - - //echo $node->name; - - - //die(); if ($node instanceof Node\Expr\Error) { $node = $node->getAttribute('parentNode'); @@ -84,7 +71,7 @@ class SignatureHelpProvider if ($node instanceof Node\Expr\FuncCall) { if ($def = $this->definitionResolver->resolveReferenceNodeToDefinition($node)) { $signature = new SignatureInformation; - $signature->label = str_replace('()', '', $def->fqn); + $signature->label = str_replace('()', '', $def->fqn) . '('.implode(', ', $def->parameters).')'; $signature->documentation = $def->documentation; $signature->parameters = []; foreach ($def->parameters as $param) { @@ -99,7 +86,7 @@ class SignatureHelpProvider $fqn = $method[0] . '()'; if ($def = $this->index->getDefinition($fqn)) { $signature = new SignatureInformation; - $signature->label = $method[0]; + $signature->label = $method[0] . '('.implode(', ', $def->parameters).')'; $signature->documentation = $def->documentation; $signature->parameters = []; foreach ($def->parameters as $param) { @@ -113,7 +100,7 @@ class SignatureHelpProvider } else if ($node instanceof Node\Expr\MethodCall) { if ($def = $this->definitionResolver->resolveReferenceNodeToDefinition($node)) { $signature = new SignatureInformation; - $signature->label = str_replace('()', '', explode('->', $def->fqn)[1]); + $signature->label = str_replace('()', '', explode('->', $def->fqn)[1]) . '('.implode(', ', $def->parameters).')'; $signature->documentation = $def->documentation; $signature->parameters = []; foreach ($def->parameters as $param) { @@ -125,12 +112,21 @@ class SignatureHelpProvider } } else if ($node instanceof Node\Expr\PropertyFetch) { if ($def = $this->definitionResolver->resolveReferenceNodeToDefinition($node->var)) { + $fqn = $def->fqn; + if (!$fqn) { + $fqns = DefinitionResolver::getFqnsFromType( + $this->definitionResolver->resolveExpressionNodeToType($node->var) + ); + if (count($fqns)) { + $fqn = $fqns[0]; + } + } $method = trim(substr($line, strrpos($line, ">") + 1)); - if ($method) { - $fqn = $def->fqn . '->' . $method . '()'; + if ($method && $fqn) { + $fqn = $fqn . '->' . $method . '()'; if ($def = $this->index->getDefinition($fqn)) { $signature = new SignatureInformation; - $signature->label = str_replace('()', '', explode('->', $def->fqn)[1]); + $signature->label = str_replace('()', '', explode('->', $def->fqn)[1]) . '('.implode(', ', $def->parameters).')'; $signature->documentation = $def->documentation; $signature->parameters = []; foreach ($def->parameters as $param) { @@ -145,7 +141,7 @@ class SignatureHelpProvider } else if ($node instanceof Node\Expr\StaticCall) { if ($def = $this->definitionResolver->resolveReferenceNodeToDefinition($node)) { $signature = new SignatureInformation; - $signature->label = str_replace('()', '', explode('::', $def->fqn)[1]); + $signature->label = str_replace('()', '', explode('::', $def->fqn)[1]) . '('.implode(', ', $def->parameters).')'; $signature->documentation = $def->documentation; $signature->parameters = []; foreach ($def->parameters as $param) { @@ -162,7 +158,7 @@ class SignatureHelpProvider $fqn = $def->fqn . '::' . $method . '()'; if ($def = $this->index->getDefinition($fqn)) { $signature = new SignatureInformation; - $signature->label = str_replace('()', '', explode('::', $def->fqn)[1]); + $signature->label = str_replace('()', '', explode('::', $def->fqn)[1]) . '('.implode(', ', $def->parameters).')'; $signature->documentation = $def->documentation; $signature->parameters = []; foreach ($def->parameters as $param) { diff --git a/tests/Server/TextDocument/SignatureHelpTest.php b/tests/Server/TextDocument/SignatureHelpTest.php index bc9c46f..23e35d5 100644 --- a/tests/Server/TextDocument/SignatureHelpTest.php +++ b/tests/Server/TextDocument/SignatureHelpTest.php @@ -57,7 +57,29 @@ class SignatureHelpTest extends TestCase $help->signatures = []; $info = new SignatureInformation; $help->signatures[] = $info; - $info->label = 'method'; + $info->label = 'method(string $param = "")'; + $info->parameters = []; + $param = new ParameterInformation; + $info->parameters[] = $param; + $param->label = 'string $param = ""'; + + $this->assertEquals($help, $result); + } + + public function testMethodClosedReference() + { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/signatureHelp/methodClosed.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $result = $this->textDocument->signatureHelp( + new TextDocumentIdentifier($completionUri), + new Position(14, 14) + )->wait(); + + $help = new SignatureHelp; + $help->signatures = []; + $info = new SignatureInformation; + $help->signatures[] = $info; + $info->label = 'method(string $param = "")'; $info->parameters = []; $param = new ParameterInformation; $info->parameters[] = $param; @@ -79,7 +101,29 @@ class SignatureHelpTest extends TestCase $help->signatures = []; $info = new SignatureInformation; $help->signatures[] = $info; - $info->label = 'method'; + $info->label = 'method(string $param = "")'; + $info->parameters = []; + $param = new ParameterInformation; + $info->parameters[] = $param; + $param->label = 'string $param = ""'; + + $this->assertEquals($help, $result); + } + + public function testMethodNotClosedReference() + { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/signatureHelp/methodNotClosed.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $result = $this->textDocument->signatureHelp( + new TextDocumentIdentifier($completionUri), + new Position(14, 14) + )->wait(); + + $help = new SignatureHelp; + $help->signatures = []; + $info = new SignatureInformation; + $help->signatures[] = $info; + $info->label = 'method(string $param = "")'; $info->parameters = []; $param = new ParameterInformation; $info->parameters[] = $param; @@ -101,7 +145,7 @@ class SignatureHelpTest extends TestCase $help->signatures = []; $info = new SignatureInformation; $help->signatures[] = $info; - $info->label = 'helpFunc1'; + $info->label = 'helpFunc1(int $count = 0)'; $info->parameters = []; $param = new ParameterInformation; $info->parameters[] = $param; @@ -123,7 +167,7 @@ class SignatureHelpTest extends TestCase $help->signatures = []; $info = new SignatureInformation; $help->signatures[] = $info; - $info->label = 'helpFunc2'; + $info->label = 'helpFunc2(int $count = 0)'; $info->parameters = []; $param = new ParameterInformation; $info->parameters[] = $param; @@ -145,7 +189,7 @@ class SignatureHelpTest extends TestCase $help->signatures = []; $info = new SignatureInformation; $help->signatures[] = $info; - $info->label = 'method'; + $info->label = 'method(string $param = "")'; $info->parameters = []; $param = new ParameterInformation; $info->parameters[] = $param; @@ -167,7 +211,7 @@ class SignatureHelpTest extends TestCase $help->signatures = []; $info = new SignatureInformation; $help->signatures[] = $info; - $info->label = 'method'; + $info->label = 'method(string $param = "")'; $info->parameters = []; $param = new ParameterInformation; $info->parameters[] = $param; From 3b4e0c32752f2d116ad537ed5b86851e64745f92 Mon Sep 17 00:00:00 2001 From: Ivan Bozhanov Date: Fri, 20 Jan 2017 15:26:14 +0200 Subject: [PATCH 08/22] added param count --- fixtures/signatureHelp/funcClosed.php | 1 + fixtures/signatureHelp/funcNotClosed.php | 3 + fixtures/signatureHelp/methodNotClosed.php | 8 +- fixtures/signatureHelp/staticNotClosed.php | 4 +- src/SignatureHelpProvider.php | 204 ++++++++++----------- 5 files changed, 114 insertions(+), 106 deletions(-) diff --git a/fixtures/signatureHelp/funcClosed.php b/fixtures/signatureHelp/funcClosed.php index 3efbd9a..6012844 100644 --- a/fixtures/signatureHelp/funcClosed.php +++ b/fixtures/signatureHelp/funcClosed.php @@ -3,4 +3,5 @@ function helpFunc1(int $count = 0) { } + helpFunc1() diff --git a/fixtures/signatureHelp/funcNotClosed.php b/fixtures/signatureHelp/funcNotClosed.php index f469937..d636f42 100644 --- a/fixtures/signatureHelp/funcNotClosed.php +++ b/fixtures/signatureHelp/funcNotClosed.php @@ -3,4 +3,7 @@ function helpFunc2(int $count = 0) { } + +$a = 1; + helpFunc2( diff --git a/fixtures/signatureHelp/methodNotClosed.php b/fixtures/signatureHelp/methodNotClosed.php index af55912..d5ce4cc 100644 --- a/fixtures/signatureHelp/methodNotClosed.php +++ b/fixtures/signatureHelp/methodNotClosed.php @@ -7,9 +7,11 @@ class HelpClass2 } public function test() { - $this->method( + $this->method(1,1); } } - $a = new HelpClass2; -$a->method(1, \ No newline at end of file +$a + ->method( + 1, + array(), diff --git a/fixtures/signatureHelp/staticNotClosed.php b/fixtures/signatureHelp/staticNotClosed.php index b2f76a0..8f0ac96 100644 --- a/fixtures/signatureHelp/staticNotClosed.php +++ b/fixtures/signatureHelp/staticNotClosed.php @@ -7,4 +7,6 @@ class HelpClass4 } } -HelpClass4::method( +HelpClass4::method(1, 2, 3 + +HelpClass4::method(1, 2, 3 diff --git a/src/SignatureHelpProvider.php b/src/SignatureHelpProvider.php index fe850d9..b7575cc 100644 --- a/src/SignatureHelpProvider.php +++ b/src/SignatureHelpProvider.php @@ -3,9 +3,11 @@ declare(strict_types = 1); namespace LanguageServer; +use PhpParser\ErrorHandler\Collecting; use PhpParser\Node; use LanguageServer\Index\ReadableIndex; use LanguageServer\Protocol\{ + Range, Position, SignatureHelp, SignatureInformation, @@ -24,6 +26,16 @@ class SignatureHelpProvider */ private $index; + /** + * @var Parser + */ + private $parser; + + /** + * @var Parser + */ + private $parserErrorHandler; + /** * @param DefinitionResolver $definitionResolver * @param ReadableIndex $index @@ -32,6 +44,8 @@ class SignatureHelpProvider { $this->definitionResolver = $definitionResolver; $this->index = $index; + $this->parser = new Parser; + $this->parserErrorHandler = new Collecting; } /** @@ -41,76 +55,52 @@ class SignatureHelpProvider * @param Position $pos The cursor position * @return SignatureHelp */ - public function provideSignature(PhpDocument $doc, Position $pos): SignatureHelp + public function provideSignature(PhpDocument $doc, Position $pos) : SignatureHelp { $help = new SignatureHelp; $help->signatures = []; - $newPos = clone $pos; - $line = explode("\n", $doc->getContent())[$newPos->line]; + $handle = fopen($doc->getUri(), 'r'); + $lines = []; + for ($i = 0; $i < $pos->line; $i++) { + $lines[] = strlen(fgets($handle)); + } + $filePos = ftell($handle) + $pos->character; + $line = substr(fgets($handle), 0, $pos->character); + fseek($handle, 0); + do { - $newPos->character --; - } while ($newPos->character > 0 && $line[$newPos->character] !== "("); + $node = $doc->getNodeAtPosition($pos); + $pos->character--; + if ($pos->character < 0) { + $pos->line --; + if ($pos->line < 0) { + break; + } + $pos->character = $lines[$pos->line]; + } + } while ($node === null); - if (!$newPos->character) { + if ($node === null) { + fclose($handle); return $help; } - $line = substr($line, 0, $newPos->character); - - $newPos->character --; - - $node = $doc->getNodeAtPosition($newPos); - - if ($node instanceof Node\Expr\Error) { + $i = 0; + while (!( + $node instanceof Node\Expr\PropertyFetch || + $node instanceof Node\Expr\MethodCall || + $node instanceof Node\Expr\FuncCall || + $node instanceof Node\Expr\ClassConstFetch || + $node instanceof Node\Expr\StaticCall + ) && ++$i < 5 && $node !== null) { $node = $node->getAttribute('parentNode'); } - - if ($node instanceof Node\Expr\Error) { - $node = $node->getAttribute('parentNode'); - } - if ($node instanceof Node\Expr\FuncCall) { - if ($def = $this->definitionResolver->resolveReferenceNodeToDefinition($node)) { - $signature = new SignatureInformation; - $signature->label = str_replace('()', '', $def->fqn) . '('.implode(', ', $def->parameters).')'; - $signature->documentation = $def->documentation; - $signature->parameters = []; - foreach ($def->parameters as $param) { - $p = new ParameterInformation; - $p->label = $param; - $signature->parameters[] = $p; - } - $help->signatures[] = $signature; - } - } else if ($node instanceof Node\Name\FullyQualified || $node === null) { - if (preg_match('([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$)', $line, $method)) { - $fqn = $method[0] . '()'; - if ($def = $this->index->getDefinition($fqn)) { - $signature = new SignatureInformation; - $signature->label = $method[0] . '('.implode(', ', $def->parameters).')'; - $signature->documentation = $def->documentation; - $signature->parameters = []; - foreach ($def->parameters as $param) { - $p = new ParameterInformation; - $p->label = $param; - $signature->parameters[] = $p; - } - $help->signatures[] = $signature; - } - } - } else if ($node instanceof Node\Expr\MethodCall) { - if ($def = $this->definitionResolver->resolveReferenceNodeToDefinition($node)) { - $signature = new SignatureInformation; - $signature->label = str_replace('()', '', explode('->', $def->fqn)[1]) . '('.implode(', ', $def->parameters).')'; - $signature->documentation = $def->documentation; - $signature->parameters = []; - foreach ($def->parameters as $param) { - $p = new ParameterInformation; - $p->label = $param; - $signature->parameters[] = $p; - } - $help->signatures[] = $signature; - } - } else if ($node instanceof Node\Expr\PropertyFetch) { + $params = ''; + if ($node instanceof Node\Expr\PropertyFetch) { + fseek($handle, $node->name->getAttribute('startFilePos')); + $method = fread($handle, ($node->name->getAttribute('endFilePos') + 1) - $node->name->getAttribute('startFilePos')); + fseek($handle, $node->name->getAttribute('endFilePos') + 1); + $params = fread($handle, ($filePos - 1) - $node->name->getAttribute('endFilePos')); if ($def = $this->definitionResolver->resolveReferenceNodeToDefinition($node->var)) { $fqn = $def->fqn; if (!$fqn) { @@ -121,55 +111,65 @@ class SignatureHelpProvider $fqn = $fqns[0]; } } - $method = trim(substr($line, strrpos($line, ">") + 1)); - if ($method && $fqn) { + if ($fqn) { $fqn = $fqn . '->' . $method . '()'; - if ($def = $this->index->getDefinition($fqn)) { - $signature = new SignatureInformation; - $signature->label = str_replace('()', '', explode('->', $def->fqn)[1]) . '('.implode(', ', $def->parameters).')'; - $signature->documentation = $def->documentation; - $signature->parameters = []; - foreach ($def->parameters as $param) { - $p = new ParameterInformation; - $p->label = $param; - $signature->parameters[] = $p; - } - $help->signatures[] = $signature; - } + $def = $this->index->getDefinition($fqn); } } + } else if ($node instanceof Node\Expr\MethodCall) { + fseek($handle, $node->getAttribute('startFilePos')); + $params = explode('(', fread($handle, $filePos - $node->getAttribute('startFilePos')), 2)[1]; + $def = $this->definitionResolver->resolveReferenceNodeToDefinition($node); + } else if ($node instanceof Node\Expr\FuncCall) { + fseek($handle, $node->getAttribute('startFilePos')); + $params = explode('(', fread($handle, $filePos - $node->getAttribute('startFilePos')), 2)[1]; + $fqn = $this->definitionResolver->resolveReferenceNodeToFqn($node->name); + $def = $this->index->getDefinition($fqn); } else if ($node instanceof Node\Expr\StaticCall) { - if ($def = $this->definitionResolver->resolveReferenceNodeToDefinition($node)) { - $signature = new SignatureInformation; - $signature->label = str_replace('()', '', explode('::', $def->fqn)[1]) . '('.implode(', ', $def->parameters).')'; - $signature->documentation = $def->documentation; - $signature->parameters = []; - foreach ($def->parameters as $param) { - $p = new ParameterInformation; - $p->label = $param; - $signature->parameters[] = $p; - } - $help->signatures[] = $signature; - } + fseek($handle, $node->getAttribute('startFilePos')); + $params = explode('(', fread($handle, $filePos - $node->getAttribute('startFilePos')), 2)[1]; + $def = $this->definitionResolver->resolveReferenceNodeToDefinition($node); } else if ($node instanceof Node\Expr\ClassConstFetch) { - if ($def = $this->definitionResolver->resolveReferenceNodeToDefinition($node->class)) { - $method = trim(substr($line, strrpos($line, ":") + 1)); - if ($method) { - $fqn = $def->fqn . '::' . $method . '()'; - if ($def = $this->index->getDefinition($fqn)) { - $signature = new SignatureInformation; - $signature->label = str_replace('()', '', explode('::', $def->fqn)[1]) . '('.implode(', ', $def->parameters).')'; - $signature->documentation = $def->documentation; - $signature->parameters = []; - foreach ($def->parameters as $param) { - $p = new ParameterInformation; - $p->label = $param; - $signature->parameters[] = $p; - } - $help->signatures[] = $signature; - } + fseek($handle, $node->name->getAttribute('endFilePos') + 2); + $params = fread($handle, ($filePos - 1) - $node->name->getAttribute('endFilePos')); + fseek($handle, $node->name->getAttribute('startFilePos')); + $method = fread($handle, ($node->name->getAttribute('endFilePos') + 1) - $node->name->getAttribute('startFilePos')); + $method = explode('::', str_replace('()', '', $method), 2); + $method = $method[1] ?? $method[0]; + $fqn = $this->definitionResolver->resolveReferenceNodeToFqn($node->class); + $def = $this->index->getDefinition($fqn.'::'.$method.'()'); + } else { + if (!preg_match('(([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*\((.*)$)', $line, $method)) { + fclose($handle); + return $help; + } + $def = $this->index->getDefinition($method[1] . '()'); + $params = $method[2]; + } + fclose($handle); + + if ($def) { + $method = preg_split('(::|->)', str_replace('()', '', $def->fqn), 2); + $method = $method[1] ?? $method[0]; + $signature = new SignatureInformation; + $signature->label = $method . '('.implode(', ', $def->parameters).')'; + $signature->documentation = $def->documentation; + $signature->parameters = []; + foreach ($def->parameters as $param) { + $p = new ParameterInformation; + $p->label = $param; + $signature->parameters[] = $p; + } + $help->activeSignature = 0; + $help->activeParameter = 0; + if (strlen(trim($params))) { + try { + $params = $this->parser->parse('parserErrorHandler)[0]->expr->items; + $help->activeParameter = count($params); + } catch (\Exception $e) { } } + $help->signatures[] = $signature; } return $help; From a7c094eacbd2b54c9fb622367ce1419e858a566b Mon Sep 17 00:00:00 2001 From: Ivan Bozhanov Date: Fri, 20 Jan 2017 15:36:10 +0200 Subject: [PATCH 09/22] fixed param counting --- src/SignatureHelpProvider.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/SignatureHelpProvider.php b/src/SignatureHelpProvider.php index b7575cc..c044a7c 100644 --- a/src/SignatureHelpProvider.php +++ b/src/SignatureHelpProvider.php @@ -162,12 +162,12 @@ class SignatureHelpProvider } $help->activeSignature = 0; $help->activeParameter = 0; + $params = ltrim($params, "( "); if (strlen(trim($params))) { try { $params = $this->parser->parse('parserErrorHandler)[0]->expr->items; - $help->activeParameter = count($params); - } catch (\Exception $e) { - } + $help->activeParameter = count($params) - 1; + } catch (\Exception $ignore) { } } $help->signatures[] = $signature; } From 8995611f6c2b2d73972ae051e0643ff42d97fb35 Mon Sep 17 00:00:00 2001 From: Ivan Bozhanov Date: Fri, 20 Jan 2017 17:00:32 +0200 Subject: [PATCH 10/22] Improved param counting --- src/SignatureHelpProvider.php | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/SignatureHelpProvider.php b/src/SignatureHelpProvider.php index c044a7c..4fbb8f0 100644 --- a/src/SignatureHelpProvider.php +++ b/src/SignatureHelpProvider.php @@ -165,8 +165,27 @@ class SignatureHelpProvider $params = ltrim($params, "( "); if (strlen(trim($params))) { try { - $params = $this->parser->parse('parserErrorHandler)[0]->expr->items; - $help->activeParameter = count($params) - 1; + $lex = new \PhpParser\Lexer(); + $lex->startLexing('parserErrorHandler); + $value = null; + $lex->getNextToken($value); + $lex->getNextToken($value); + $lex->getNextToken($value); + $params = 0; + $stack = []; + while ($value !== "\0") { + $lex->getNextToken($value); + if ($value === ',' && !count($stack)) { + $help->activeParameter++; + } + if ($value === '(') { + $stack[] = ')'; + } else if ($value === '[') { + $stack[] = ']'; + } else if (count($stack) && $value === $stack[count($stack)-1]) { + array_pop($stack); + } + } } catch (\Exception $ignore) { } } $help->signatures[] = $signature; From 8fbbf3d064d17c8436da8b5c6b98ed1c180c4883 Mon Sep 17 00:00:00 2001 From: Ivan Bozhanov Date: Fri, 20 Jan 2017 17:04:14 +0200 Subject: [PATCH 11/22] Updated tests --- tests/Server/TextDocument/SignatureHelpTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/Server/TextDocument/SignatureHelpTest.php b/tests/Server/TextDocument/SignatureHelpTest.php index 23e35d5..87eed2a 100644 --- a/tests/Server/TextDocument/SignatureHelpTest.php +++ b/tests/Server/TextDocument/SignatureHelpTest.php @@ -77,6 +77,8 @@ class SignatureHelpTest extends TestCase $help = new SignatureHelp; $help->signatures = []; + $help->activeSignature = 0; + $help->activeParameter = 1; $info = new SignatureInformation; $help->signatures[] = $info; $info->label = 'method(string $param = "")'; From d433ae4ed0c4bce74b8b6b91c7d426e56582b12d Mon Sep 17 00:00:00 2001 From: Ivan Bozhanov Date: Fri, 20 Jan 2017 17:33:04 +0200 Subject: [PATCH 12/22] Now works with unsaved files --- src/SignatureHelpProvider.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/SignatureHelpProvider.php b/src/SignatureHelpProvider.php index 4fbb8f0..67336d4 100644 --- a/src/SignatureHelpProvider.php +++ b/src/SignatureHelpProvider.php @@ -60,7 +60,10 @@ class SignatureHelpProvider $help = new SignatureHelp; $help->signatures = []; - $handle = fopen($doc->getUri(), 'r'); + $handle = fopen('php://temp', 'r+'); + fwrite($handle, $doc->getContent()); + fseek($handle, 0); + $lines = []; for ($i = 0; $i < $pos->line; $i++) { $lines[] = strlen(fgets($handle)); From 75e5e249da634999a11bf92792df1f808d2f6c49 Mon Sep 17 00:00:00 2001 From: Ivan Bozhanov Date: Fri, 20 Jan 2017 17:44:25 +0200 Subject: [PATCH 13/22] Properly hiding when done with params --- src/SignatureHelpProvider.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/SignatureHelpProvider.php b/src/SignatureHelpProvider.php index 67336d4..0076b4a 100644 --- a/src/SignatureHelpProvider.php +++ b/src/SignatureHelpProvider.php @@ -178,6 +178,9 @@ class SignatureHelpProvider $stack = []; while ($value !== "\0") { $lex->getNextToken($value); + if (($value === ")" || $value === ";") && !count($stack)) { + return $help; + } if ($value === ',' && !count($stack)) { $help->activeParameter++; } @@ -191,7 +194,9 @@ class SignatureHelpProvider } } catch (\Exception $ignore) { } } - $help->signatures[] = $signature; + if ($help->activeParameter < count($signature->parameters)) { + $help->signatures[] = $signature; + } } return $help; From 6980fb5da7f92509ef37da4264ba4cbbdd388a9d Mon Sep 17 00:00:00 2001 From: Ivan Bozhanov Date: Fri, 20 Jan 2017 17:47:18 +0200 Subject: [PATCH 14/22] Updated tests --- fixtures/signatureHelp/methodClosed.php | 2 +- tests/Server/TextDocument/SignatureHelpTest.php | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/fixtures/signatureHelp/methodClosed.php b/fixtures/signatureHelp/methodClosed.php index a7430a5..924cd30 100644 --- a/fixtures/signatureHelp/methodClosed.php +++ b/fixtures/signatureHelp/methodClosed.php @@ -12,4 +12,4 @@ class HelpClass1 } $a = new HelpClass1; -$a->method(1, ); \ No newline at end of file +$a->method(); \ No newline at end of file diff --git a/tests/Server/TextDocument/SignatureHelpTest.php b/tests/Server/TextDocument/SignatureHelpTest.php index 87eed2a..33b10f5 100644 --- a/tests/Server/TextDocument/SignatureHelpTest.php +++ b/tests/Server/TextDocument/SignatureHelpTest.php @@ -72,13 +72,11 @@ class SignatureHelpTest extends TestCase $this->loader->open($completionUri, file_get_contents($completionUri)); $result = $this->textDocument->signatureHelp( new TextDocumentIdentifier($completionUri), - new Position(14, 14) + new Position(14, 11) )->wait(); $help = new SignatureHelp; $help->signatures = []; - $help->activeSignature = 0; - $help->activeParameter = 1; $info = new SignatureInformation; $help->signatures[] = $info; $info->label = 'method(string $param = "")'; From b0f395284434004dd516f5fc34ee79a895ddfea9 Mon Sep 17 00:00:00 2001 From: Ivan Bozhanov Date: Fri, 20 Jan 2017 20:33:57 +0200 Subject: [PATCH 15/22] Fixed phpcs error --- src/SignatureHelpProvider.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/SignatureHelpProvider.php b/src/SignatureHelpProvider.php index 0076b4a..b67df97 100644 --- a/src/SignatureHelpProvider.php +++ b/src/SignatureHelpProvider.php @@ -192,7 +192,8 @@ class SignatureHelpProvider array_pop($stack); } } - } catch (\Exception $ignore) { } + } catch (\Exception $ignore) { + } } if ($help->activeParameter < count($signature->parameters)) { $help->signatures[] = $signature; From 1b68c73186158df2e64879781952255255f96d39 Mon Sep 17 00:00:00 2001 From: Ivan Bozhanov Date: Fri, 20 Jan 2017 22:31:18 +0200 Subject: [PATCH 16/22] Fixed hiding the tooltip too early, fixed tests --- fixtures/signatureHelp/funcNotClosed.php | 2 -- fixtures/signatureHelp/staticNotClosed.php | 4 +-- src/SignatureHelpProvider.php | 28 +++++++++++-------- .../Server/TextDocument/SignatureHelpTest.php | 12 ++++---- 4 files changed, 24 insertions(+), 22 deletions(-) diff --git a/fixtures/signatureHelp/funcNotClosed.php b/fixtures/signatureHelp/funcNotClosed.php index d636f42..95d7ebb 100644 --- a/fixtures/signatureHelp/funcNotClosed.php +++ b/fixtures/signatureHelp/funcNotClosed.php @@ -4,6 +4,4 @@ function helpFunc2(int $count = 0) { } -$a = 1; - helpFunc2( diff --git a/fixtures/signatureHelp/staticNotClosed.php b/fixtures/signatureHelp/staticNotClosed.php index 8f0ac96..49728aa 100644 --- a/fixtures/signatureHelp/staticNotClosed.php +++ b/fixtures/signatureHelp/staticNotClosed.php @@ -7,6 +7,4 @@ class HelpClass4 } } -HelpClass4::method(1, 2, 3 - -HelpClass4::method(1, 2, 3 +HelpClass4::method(1 diff --git a/src/SignatureHelpProvider.php b/src/SignatureHelpProvider.php index b67df97..1bfedd2 100644 --- a/src/SignatureHelpProvider.php +++ b/src/SignatureHelpProvider.php @@ -72,8 +72,13 @@ class SignatureHelpProvider $line = substr(fgets($handle), 0, $pos->character); fseek($handle, 0); + $i = 0; + $orig = null; do { $node = $doc->getNodeAtPosition($pos); + if ($node !== null) { + $orig = $node; + } $pos->character--; if ($pos->character < 0) { $pos->line --; @@ -82,22 +87,23 @@ class SignatureHelpProvider } $pos->character = $lines[$pos->line]; } - } while ($node === null); - - if ($node === null) { - fclose($handle); - return $help; - } - $i = 0; - while (!( + } while (!( $node instanceof Node\Expr\PropertyFetch || $node instanceof Node\Expr\MethodCall || $node instanceof Node\Expr\FuncCall || $node instanceof Node\Expr\ClassConstFetch || $node instanceof Node\Expr\StaticCall - ) && ++$i < 5 && $node !== null) { - $node = $node->getAttribute('parentNode'); + ) && ++$i < 120); + + if ($node === null) { + $node = $orig; } + + if ($node === null) { + fclose($handle); + return $help; + } + $params = ''; if ($node instanceof Node\Expr\PropertyFetch) { fseek($handle, $node->name->getAttribute('startFilePos')); @@ -142,7 +148,7 @@ class SignatureHelpProvider $fqn = $this->definitionResolver->resolveReferenceNodeToFqn($node->class); $def = $this->index->getDefinition($fqn.'::'.$method.'()'); } else { - if (!preg_match('(([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*\((.*)$)', $line, $method)) { + if (!preg_match('(([a-zA-Z_\x7f-\xff][:a-zA-Z0-9_\x7f-\xff]*)\s*\((.*)$)', $line, $method)) { fclose($handle); return $help; } diff --git a/tests/Server/TextDocument/SignatureHelpTest.php b/tests/Server/TextDocument/SignatureHelpTest.php index 33b10f5..a6f4620 100644 --- a/tests/Server/TextDocument/SignatureHelpTest.php +++ b/tests/Server/TextDocument/SignatureHelpTest.php @@ -132,13 +132,13 @@ class SignatureHelpTest extends TestCase $this->assertEquals($help, $result); } - public function funcClosed() + public function testFuncClosed() { $completionUri = pathToUri(__DIR__ . '/../../../fixtures/signatureHelp/funcClosed.php'); $this->loader->open($completionUri, file_get_contents($completionUri)); $result = $this->textDocument->signatureHelp( new TextDocumentIdentifier($completionUri), - new Position(5, 10) + new Position(6, 10) )->wait(); $help = new SignatureHelp; @@ -154,13 +154,13 @@ class SignatureHelpTest extends TestCase $this->assertEquals($help, $result); } - public function funcNotClosed() + public function testFuncNotClosed() { $completionUri = pathToUri(__DIR__ . '/../../../fixtures/signatureHelp/funcNotClosed.php'); $this->loader->open($completionUri, file_get_contents($completionUri)); $result = $this->textDocument->signatureHelp( new TextDocumentIdentifier($completionUri), - new Position(5, 10) + new Position(6, 10) )->wait(); $help = new SignatureHelp; @@ -176,7 +176,7 @@ class SignatureHelpTest extends TestCase $this->assertEquals($help, $result); } - public function staticClosed() + public function testStaticClosed() { $completionUri = pathToUri(__DIR__ . '/../../../fixtures/signatureHelp/staticClosed.php'); $this->loader->open($completionUri, file_get_contents($completionUri)); @@ -198,7 +198,7 @@ class SignatureHelpTest extends TestCase $this->assertEquals($help, $result); } - public function staticNotClosed() + public function testStaticNotClosed() { $completionUri = pathToUri(__DIR__ . '/../../../fixtures/signatureHelp/staticNotClosed.php'); $this->loader->open($completionUri, file_get_contents($completionUri)); From 96948c44bda9cc78753e7583721b8c93928ffae3 Mon Sep 17 00:00:00 2001 From: Ivan Bozhanov Date: Sat, 21 Jan 2017 20:55:57 +0200 Subject: [PATCH 17/22] Added comma as a trigger character --- src/LanguageServer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LanguageServer.php b/src/LanguageServer.php index bb97d69..1a398e2 100644 --- a/src/LanguageServer.php +++ b/src/LanguageServer.php @@ -250,7 +250,7 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher $serverCapabilities->completionProvider->triggerCharacters = ['$', '>']; // Support "Signature Help" $serverCapabilities->signatureHelpProvider = new SignatureHelpOptions; - $serverCapabilities->signatureHelpProvider->triggerCharacters = ['(']; + $serverCapabilities->signatureHelpProvider->triggerCharacters = ['(',',']; // Support global references $serverCapabilities->xworkspaceReferencesProvider = true; $serverCapabilities->xdefinitionProvider = true; From 9e822fbb614f9f8cc8d3a06f7109112f06850b51 Mon Sep 17 00:00:00 2001 From: Ivan Bozhanov Date: Sat, 21 Jan 2017 20:58:37 +0200 Subject: [PATCH 18/22] Removed parser instance --- src/SignatureHelpProvider.php | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/SignatureHelpProvider.php b/src/SignatureHelpProvider.php index 1bfedd2..b066e68 100644 --- a/src/SignatureHelpProvider.php +++ b/src/SignatureHelpProvider.php @@ -26,16 +26,6 @@ class SignatureHelpProvider */ private $index; - /** - * @var Parser - */ - private $parser; - - /** - * @var Parser - */ - private $parserErrorHandler; - /** * @param DefinitionResolver $definitionResolver * @param ReadableIndex $index @@ -44,8 +34,6 @@ class SignatureHelpProvider { $this->definitionResolver = $definitionResolver; $this->index = $index; - $this->parser = new Parser; - $this->parserErrorHandler = new Collecting; } /** @@ -71,7 +59,7 @@ class SignatureHelpProvider $filePos = ftell($handle) + $pos->character; $line = substr(fgets($handle), 0, $pos->character); fseek($handle, 0); - + $i = 0; $orig = null; do { @@ -103,7 +91,7 @@ class SignatureHelpProvider fclose($handle); return $help; } - + $params = ''; if ($node instanceof Node\Expr\PropertyFetch) { fseek($handle, $node->name->getAttribute('startFilePos')); @@ -175,7 +163,7 @@ class SignatureHelpProvider if (strlen(trim($params))) { try { $lex = new \PhpParser\Lexer(); - $lex->startLexing('parserErrorHandler); + $lex->startLexing('getNextToken($value); $lex->getNextToken($value); From df0c9d405d998a857f5e5614b44868e521746c91 Mon Sep 17 00:00:00 2001 From: Ivan Bozhanov Date: Sat, 21 Jan 2017 22:25:44 +0200 Subject: [PATCH 19/22] Added constructors --- fixtures/signatureHelp/methodClosed.php | 2 +- src/DefinitionResolver.php | 9 +- src/Protocol/ParameterInformation.php | 10 + src/Protocol/SignatureHelp.php | 12 ++ src/Protocol/SignatureInformation.php | 12 ++ src/SignatureHelpProvider.php | 42 ++--- tests/LanguageServerTest.php | 2 +- .../Server/TextDocument/SignatureHelpTest.php | 176 +++++++++--------- 8 files changed, 153 insertions(+), 112 deletions(-) diff --git a/fixtures/signatureHelp/methodClosed.php b/fixtures/signatureHelp/methodClosed.php index 924cd30..0f0890f 100644 --- a/fixtures/signatureHelp/methodClosed.php +++ b/fixtures/signatureHelp/methodClosed.php @@ -12,4 +12,4 @@ class HelpClass1 } $a = new HelpClass1; -$a->method(); \ No newline at end of file +$a->method(); diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index 58bc9e9..bb5fd6c 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -7,6 +7,7 @@ use PhpParser\Node; use PhpParser\PrettyPrinter\Standard as PrettyPrinter; use phpDocumentor\Reflection\{Types, Type, Fqsen, TypeResolver}; use LanguageServer\Protocol\SymbolInformation; +use LanguageServer\Protocol\ParameterInformation; use LanguageServer\Index\ReadableIndex; class DefinitionResolver @@ -134,7 +135,13 @@ class DefinitionResolver $def->parameters = []; if ($node instanceof Node\FunctionLike) { foreach ($node->getParams() as $param) { - $def->parameters[] = $this->prettyPrinter->prettyPrint([$param]); + if (!$param->getAttribute('parentNode')) { + $param->setAttribute('parentNode', $node); + } + $def->parameters[] = new ParameterInformation( + $this->prettyPrinter->prettyPrint([$param]), + $this->getDocumentationFromNode($param) + ); } } return $def; diff --git a/src/Protocol/ParameterInformation.php b/src/Protocol/ParameterInformation.php index 89b5e53..628804a 100644 --- a/src/Protocol/ParameterInformation.php +++ b/src/Protocol/ParameterInformation.php @@ -23,4 +23,14 @@ class ParameterInformation * @var string|null */ public $documentation; + + /** + * @param string $label The label of this signature. Will be shown in the UI. + * @param string|null $documentation The human-readable doc-comment of this signature. + */ + public function __construct(string $label = null, string $documentation = null) + { + $this->label = $label; + $this->documentation = $documentation; + } } diff --git a/src/Protocol/SignatureHelp.php b/src/Protocol/SignatureHelp.php index 407b25a..0e8e934 100644 --- a/src/Protocol/SignatureHelp.php +++ b/src/Protocol/SignatureHelp.php @@ -29,4 +29,16 @@ class SignatureHelp * @var int|null */ public $activeParameter; + + /** + * @param SignatureInformation[] $signatures The signatures. + * @param bool $activeSignature The active signature. + * @param bool $activeParameter The active parameter of the active signature. + */ + public function __construct(array $signatures = [], int $activeSignature = 0, int $activeParameter = 0) + { + $this->signatures = $signatures; + $this->activeSignature = $activeSignature; + $this->activeParameter = $activeParameter; + } } diff --git a/src/Protocol/SignatureInformation.php b/src/Protocol/SignatureInformation.php index 77e152c..e9762c7 100644 --- a/src/Protocol/SignatureInformation.php +++ b/src/Protocol/SignatureInformation.php @@ -31,4 +31,16 @@ class SignatureInformation * @var ParameterInformation[]|null */ public $parameters; + + /** + * @param string $label The label of this signature. Will be shown in the UI. + * @param string|null $documentation The human-readable doc-comment of this signature. + * @param ParameterInformation[] $parameters The parameters of this signature. + */ + public function __construct(string $label = null, string $documentation = null, array $parameters = []) + { + $this->label = $label; + $this->documentation = $documentation; + $this->parameters = $parameters; + } } diff --git a/src/SignatureHelpProvider.php b/src/SignatureHelpProvider.php index b066e68..b2788d1 100644 --- a/src/SignatureHelpProvider.php +++ b/src/SignatureHelpProvider.php @@ -45,9 +45,6 @@ class SignatureHelpProvider */ public function provideSignature(PhpDocument $doc, Position $pos) : SignatureHelp { - $help = new SignatureHelp; - $help->signatures = []; - $handle = fopen('php://temp', 'r+'); fwrite($handle, $doc->getContent()); fseek($handle, 0); @@ -89,7 +86,7 @@ class SignatureHelpProvider if ($node === null) { fclose($handle); - return $help; + return new SignatureHelp; } $params = ''; @@ -138,7 +135,7 @@ class SignatureHelpProvider } else { if (!preg_match('(([a-zA-Z_\x7f-\xff][:a-zA-Z0-9_\x7f-\xff]*)\s*\((.*)$)', $line, $method)) { fclose($handle); - return $help; + return new SignatureHelp; } $def = $this->index->getDefinition($method[1] . '()'); $params = $method[2]; @@ -148,18 +145,8 @@ class SignatureHelpProvider if ($def) { $method = preg_split('(::|->)', str_replace('()', '', $def->fqn), 2); $method = $method[1] ?? $method[0]; - $signature = new SignatureInformation; - $signature->label = $method . '('.implode(', ', $def->parameters).')'; - $signature->documentation = $def->documentation; - $signature->parameters = []; - foreach ($def->parameters as $param) { - $p = new ParameterInformation; - $p->label = $param; - $signature->parameters[] = $p; - } - $help->activeSignature = 0; - $help->activeParameter = 0; $params = ltrim($params, "( "); + $activeParameter = 0; if (strlen(trim($params))) { try { $lex = new \PhpParser\Lexer(); @@ -173,10 +160,10 @@ class SignatureHelpProvider while ($value !== "\0") { $lex->getNextToken($value); if (($value === ")" || $value === ";") && !count($stack)) { - return $help; + return new SignatureHelp; } if ($value === ',' && !count($stack)) { - $help->activeParameter++; + $activeParameter++; } if ($value === '(') { $stack[] = ')'; @@ -189,11 +176,24 @@ class SignatureHelpProvider } catch (\Exception $ignore) { } } - if ($help->activeParameter < count($signature->parameters)) { - $help->signatures[] = $signature; + if ($activeParameter < count($def->parameters)) { + $params = array_map(function ($v) { + return $v->label; + }, $def->parameters); + return new SignatureHelp( + [ + new SignatureInformation( + $method . '('.implode(', ', $params).')', + $def->documentation, + $def->parameters + ) + ], + 0, + $activeParameter + ); } } - return $help; + return new SignatureHelp; } } diff --git a/tests/LanguageServerTest.php b/tests/LanguageServerTest.php index a32c845..2f1a6e2 100644 --- a/tests/LanguageServerTest.php +++ b/tests/LanguageServerTest.php @@ -43,7 +43,7 @@ class LanguageServerTest extends TestCase $serverCapabilities->completionProvider->resolveProvider = false; $serverCapabilities->completionProvider->triggerCharacters = ['$', '>']; $serverCapabilities->signatureHelpProvider = new SignatureHelpOptions; - $serverCapabilities->signatureHelpProvider->triggerCharacters = ['(']; + $serverCapabilities->signatureHelpProvider->triggerCharacters = ['(',',']; $serverCapabilities->xworkspaceReferencesProvider = true; $serverCapabilities->xdefinitionProvider = true; $serverCapabilities->xdependenciesProvider = true; diff --git a/tests/Server/TextDocument/SignatureHelpTest.php b/tests/Server/TextDocument/SignatureHelpTest.php index a6f4620..3814c09 100644 --- a/tests/Server/TextDocument/SignatureHelpTest.php +++ b/tests/Server/TextDocument/SignatureHelpTest.php @@ -53,17 +53,17 @@ class SignatureHelpTest extends TestCase new Position(9, 22) )->wait(); - $help = new SignatureHelp; - $help->signatures = []; - $info = new SignatureInformation; - $help->signatures[] = $info; - $info->label = 'method(string $param = "")'; - $info->parameters = []; - $param = new ParameterInformation; - $info->parameters[] = $param; - $param->label = 'string $param = ""'; - - $this->assertEquals($help, $result); + $this->assertEquals(new SignatureHelp( + [ + new SignatureInformation( + 'method(string $param = "")', + null, + [ + new ParameterInformation('string $param = ""') + ] + ) + ] + ), $result); } public function testMethodClosedReference() @@ -75,17 +75,17 @@ class SignatureHelpTest extends TestCase new Position(14, 11) )->wait(); - $help = new SignatureHelp; - $help->signatures = []; - $info = new SignatureInformation; - $help->signatures[] = $info; - $info->label = 'method(string $param = "")'; - $info->parameters = []; - $param = new ParameterInformation; - $info->parameters[] = $param; - $param->label = 'string $param = ""'; - - $this->assertEquals($help, $result); + $this->assertEquals(new SignatureHelp( + [ + new SignatureInformation( + 'method(string $param = "")', + null, + [ + new ParameterInformation('string $param = ""') + ] + ) + ] + ), $result); } public function testMethodNotClosed() @@ -97,17 +97,17 @@ class SignatureHelpTest extends TestCase new Position(9, 22) )->wait(); - $help = new SignatureHelp; - $help->signatures = []; - $info = new SignatureInformation; - $help->signatures[] = $info; - $info->label = 'method(string $param = "")'; - $info->parameters = []; - $param = new ParameterInformation; - $info->parameters[] = $param; - $param->label = 'string $param = ""'; - - $this->assertEquals($help, $result); + $this->assertEquals(new SignatureHelp( + [ + new SignatureInformation( + 'method(string $param = "")', + null, + [ + new ParameterInformation('string $param = ""') + ] + ) + ] + ), $result); } public function testMethodNotClosedReference() @@ -119,17 +119,17 @@ class SignatureHelpTest extends TestCase new Position(14, 14) )->wait(); - $help = new SignatureHelp; - $help->signatures = []; - $info = new SignatureInformation; - $help->signatures[] = $info; - $info->label = 'method(string $param = "")'; - $info->parameters = []; - $param = new ParameterInformation; - $info->parameters[] = $param; - $param->label = 'string $param = ""'; - - $this->assertEquals($help, $result); + $this->assertEquals(new SignatureHelp( + [ + new SignatureInformation( + 'method(string $param = "")', + null, + [ + new ParameterInformation('string $param = ""') + ] + ) + ] + ), $result); } public function testFuncClosed() @@ -141,17 +141,17 @@ class SignatureHelpTest extends TestCase new Position(6, 10) )->wait(); - $help = new SignatureHelp; - $help->signatures = []; - $info = new SignatureInformation; - $help->signatures[] = $info; - $info->label = 'helpFunc1(int $count = 0)'; - $info->parameters = []; - $param = new ParameterInformation; - $info->parameters[] = $param; - $param->label = 'int $count = 0'; - - $this->assertEquals($help, $result); + $this->assertEquals(new SignatureHelp( + [ + new SignatureInformation( + 'helpFunc1(int $count = 0)', + null, + [ + new ParameterInformation('int $count = 0') + ] + ) + ] + ), $result); } public function testFuncNotClosed() @@ -163,17 +163,17 @@ class SignatureHelpTest extends TestCase new Position(6, 10) )->wait(); - $help = new SignatureHelp; - $help->signatures = []; - $info = new SignatureInformation; - $help->signatures[] = $info; - $info->label = 'helpFunc2(int $count = 0)'; - $info->parameters = []; - $param = new ParameterInformation; - $info->parameters[] = $param; - $param->label = 'int $count = 0'; - - $this->assertEquals($help, $result); + $this->assertEquals(new SignatureHelp( + [ + new SignatureInformation( + 'helpFunc2(int $count = 0)', + null, + [ + new ParameterInformation('int $count = 0') + ] + ) + ] + ), $result); } public function testStaticClosed() @@ -185,17 +185,17 @@ class SignatureHelpTest extends TestCase new Position(9, 19) )->wait(); - $help = new SignatureHelp; - $help->signatures = []; - $info = new SignatureInformation; - $help->signatures[] = $info; - $info->label = 'method(string $param = "")'; - $info->parameters = []; - $param = new ParameterInformation; - $info->parameters[] = $param; - $param->label = 'string $param = ""'; - - $this->assertEquals($help, $result); + $this->assertEquals(new SignatureHelp( + [ + new SignatureInformation( + 'method(string $param = "")', + null, + [ + new ParameterInformation('string $param = ""') + ] + ) + ] + ), $result); } public function testStaticNotClosed() @@ -207,16 +207,16 @@ class SignatureHelpTest extends TestCase new Position(9, 19) )->wait(); - $help = new SignatureHelp; - $help->signatures = []; - $info = new SignatureInformation; - $help->signatures[] = $info; - $info->label = 'method(string $param = "")'; - $info->parameters = []; - $param = new ParameterInformation; - $info->parameters[] = $param; - $param->label = 'string $param = ""'; - - $this->assertEquals($help, $result); + $this->assertEquals(new SignatureHelp( + [ + new SignatureInformation( + 'method(string $param = "")', + null, + [ + new ParameterInformation('string $param = ""') + ] + ) + ] + ), $result); } } From 412a88428ee9d29aad287e2ea31a4c2bf2b396c1 Mon Sep 17 00:00:00 2001 From: Ivan Bozhanov Date: Sat, 21 Jan 2017 22:47:47 +0200 Subject: [PATCH 20/22] Fixed original node reference --- src/SignatureHelpProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SignatureHelpProvider.php b/src/SignatureHelpProvider.php index b2788d1..c29c2e8 100644 --- a/src/SignatureHelpProvider.php +++ b/src/SignatureHelpProvider.php @@ -61,7 +61,7 @@ class SignatureHelpProvider $orig = null; do { $node = $doc->getNodeAtPosition($pos); - if ($node !== null) { + if ($node !== null && $orig === null) { $orig = $node; } $pos->character--; From 549645b72dd09ea4eed3832d831083204beb79d2 Mon Sep 17 00:00:00 2001 From: Ivan Bozhanov Date: Sat, 21 Jan 2017 23:16:27 +0200 Subject: [PATCH 21/22] Fixed doc blocks --- src/Protocol/SignatureHelp.php | 6 +++--- src/Protocol/SignatureInformation.php | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Protocol/SignatureHelp.php b/src/Protocol/SignatureHelp.php index 0e8e934..9c6f3ea 100644 --- a/src/Protocol/SignatureHelp.php +++ b/src/Protocol/SignatureHelp.php @@ -32,10 +32,10 @@ class SignatureHelp /** * @param SignatureInformation[] $signatures The signatures. - * @param bool $activeSignature The active signature. - * @param bool $activeParameter The active parameter of the active signature. + * @param int|null $activeSignature The active signature. + * @param int|null $activeParameter The active parameter of the active signature. */ - public function __construct(array $signatures = [], int $activeSignature = 0, int $activeParameter = 0) + public function __construct(array $signatures = [], int $activeSignature = null, int $activeParameter = null) { $this->signatures = $signatures; $this->activeSignature = $activeSignature; diff --git a/src/Protocol/SignatureInformation.php b/src/Protocol/SignatureInformation.php index e9762c7..a8ff813 100644 --- a/src/Protocol/SignatureInformation.php +++ b/src/Protocol/SignatureInformation.php @@ -33,9 +33,9 @@ class SignatureInformation public $parameters; /** - * @param string $label The label of this signature. Will be shown in the UI. - * @param string|null $documentation The human-readable doc-comment of this signature. - * @param ParameterInformation[] $parameters The parameters of this signature. + * @param string $label The label of this signature. Will be shown in the UI. + * @param string|null $documentation The human-readable doc-comment of this signature. + * @param ParameterInformation[]|null $parameters The parameters of this signature. */ public function __construct(string $label = null, string $documentation = null, array $parameters = []) { From 32b9984e9450d190c3e2d45e7710d1db2a1431c3 Mon Sep 17 00:00:00 2001 From: Ivan Bozhanov Date: Thu, 26 Jan 2017 18:56:04 +0200 Subject: [PATCH 22/22] Fixed default value for parameters in SignatureInformation --- src/Protocol/SignatureInformation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Protocol/SignatureInformation.php b/src/Protocol/SignatureInformation.php index a8ff813..e1d4273 100644 --- a/src/Protocol/SignatureInformation.php +++ b/src/Protocol/SignatureInformation.php @@ -37,7 +37,7 @@ class SignatureInformation * @param string|null $documentation The human-readable doc-comment of this signature. * @param ParameterInformation[]|null $parameters The parameters of this signature. */ - public function __construct(string $label = null, string $documentation = null, array $parameters = []) + public function __construct(string $label = null, string $documentation = null, array $parameters = null) { $this->label = $label; $this->documentation = $documentation;