diff --git a/README.md b/README.md index fecd6c7..37800e0 100644 --- a/README.md +++ b/README.md @@ -180,7 +180,7 @@ Example: - [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 (nvim-cm-php-language-server)](https://github.com/roxma/nvim-cm-php-language-server) + - NeoVim: [LanguageServer-php-neovim](https://github.com/roxma/LanguageServer-php-neovim) with [LanguageClient neovim](https://github.com/autozimu/LanguageClient-neovim) ## Contributing diff --git a/composer.json b/composer.json index 6793606..1f23bca 100644 --- a/composer.json +++ b/composer.json @@ -28,11 +28,11 @@ }, "require": { "php": ">=7.0", - "nikic/php-parser": "^3.0", + "nikic/php-parser": "^3.0.5", "phpdocumentor/reflection-docblock": "^3.0", "sabre/event": "^5.0", "felixfbecker/advanced-json-rpc": "^2.0", - "squizlabs/php_codesniffer" : "^3.0", + "squizlabs/php_codesniffer" : "3.0.0RC3", "netresearch/jsonmapper": "^1.0", "webmozart/path-util": "^2.3", "webmozart/glob": "^4.1", diff --git a/fixtures/completion/bare_php.php b/fixtures/completion/bare_php.php new file mode 100644 index 0000000..36b5ad9 --- /dev/null +++ b/fixtures/completion/bare_php.php @@ -0,0 +1,5 @@ +./src + + + diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index 8392260..709736d 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -12,7 +12,7 @@ use LanguageServer\Index\ReadableIndex; class DefinitionResolver { /** - * @var \LanguageServer\Index + * @var \LanguageServer\Index\ReadableIndex */ private $index; @@ -48,6 +48,7 @@ class DefinitionResolver // Properties and constants can have multiple declarations // Use the parent node (that includes the modifiers), but only render the requested declaration $child = $node; + /** @var Node */ $node = $node->getAttribute('parentNode'); $defLine = clone $node; $defLine->props = [$child]; @@ -88,7 +89,14 @@ class DefinitionResolver } else { $docBlock = $node->getAttribute('docBlock'); if ($docBlock !== null) { - return $docBlock->getSummary(); + // check wether we have a description, when true, add a new paragraph + // with the description + $description = $docBlock->getDescription()->render(); + + if (empty($description)) { + return $docBlock->getSummary(); + } + return $docBlock->getSummary() . "\n\n" . $description; } } } @@ -363,7 +371,7 @@ class DefinitionResolver * Returns the assignment or parameter node where a variable was defined * * @param Node\Expr\Variable|Node\Expr\ClosureUse $var The variable access - * @return Node\Expr\Assign|Node\Param|Node\Expr\ClosureUse|null + * @return Node\Expr\Assign|Node\Expr\AssignOp|Node\Param|Node\Expr\ClosureUse|null */ public static function resolveVariableToNode(Node\Expr $var) { @@ -415,7 +423,7 @@ class DefinitionResolver * If the type could not be resolved, returns Types\Mixed. * * @param \PhpParser\Node\Expr $expr - * @return \phpDocumentor\Type + * @return \phpDocumentor\Reflection\Type */ public function resolveExpressionNodeToType(Node\Expr $expr): Type { @@ -438,7 +446,7 @@ class DefinitionResolver // Cannot get type for dynamic function call return new Types\Mixed; } - $fqn = (string)($expr->getAttribute('namespacedName') ?? $expr->name); + $fqn = (string)($expr->getAttribute('namespacedName') ?? $expr->name) . '()'; $def = $this->index->getDefinition($fqn, true); if ($def !== null) { return $def->type; @@ -539,7 +547,7 @@ class DefinitionResolver ]); } if ( - $expr instanceof Node\Expr\InstanceOf_ + $expr instanceof Node\Expr\Instanceof_ || $expr instanceof Node\Expr\Cast\Bool_ || $expr instanceof Node\Expr\BooleanNot || $expr instanceof Node\Expr\Empty_ @@ -559,19 +567,18 @@ class DefinitionResolver return new Types\Boolean; } if ( - $expr instanceof Node\Expr\Concat - || $expr instanceof Node\Expr\Cast\String_ + $expr instanceof Node\Expr\Cast\String_ || $expr instanceof Node\Expr\BinaryOp\Concat || $expr instanceof Node\Expr\AssignOp\Concat - || $expr instanceof Node\Expr\Scalar\String_ - || $expr instanceof Node\Expr\Scalar\Encapsed - || $expr instanceof Node\Expr\Scalar\EncapsedStringPart - || $expr instanceof Node\Expr\Scalar\MagicConst\Class_ - || $expr instanceof Node\Expr\Scalar\MagicConst\Dir - || $expr instanceof Node\Expr\Scalar\MagicConst\Function_ - || $expr instanceof Node\Expr\Scalar\MagicConst\Method - || $expr instanceof Node\Expr\Scalar\MagicConst\Namespace_ - || $expr instanceof Node\Expr\Scalar\MagicConst\Trait_ + || $expr instanceof Node\Scalar\String_ + || $expr instanceof Node\Scalar\Encapsed + || $expr instanceof Node\Scalar\EncapsedStringPart + || $expr instanceof Node\Scalar\MagicConst\Class_ + || $expr instanceof Node\Scalar\MagicConst\Dir + || $expr instanceof Node\Scalar\MagicConst\Function_ + || $expr instanceof Node\Scalar\MagicConst\Method + || $expr instanceof Node\Scalar\MagicConst\Namespace_ + || $expr instanceof Node\Scalar\MagicConst\Trait_ ) { return new Types\String_; } @@ -580,23 +587,35 @@ class DefinitionResolver || $expr instanceof Node\Expr\BinaryOp\Plus || $expr instanceof Node\Expr\BinaryOp\Pow || $expr instanceof Node\Expr\BinaryOp\Mul - || $expr instanceof Node\Expr\AssignOp\Minus - || $expr instanceof Node\Expr\AssignOp\Plus - || $expr instanceof Node\Expr\AssignOp\Pow - || $expr instanceof Node\Expr\AssignOp\Mul ) { if ( - $this->resolveExpressionNodeToType($expr->left) instanceof Types\Integer_ - && $this->resolveExpressionNodeToType($expr->right) instanceof Types\Integer_ + $this->resolveExpressionNodeToType($expr->left) instanceof Types\Integer + && $this->resolveExpressionNodeToType($expr->right) instanceof Types\Integer ) { return new Types\Integer; } return new Types\Float_; } + + if ( + $expr instanceof Node\Expr\AssignOp\Minus + || $expr instanceof Node\Expr\AssignOp\Plus + || $expr instanceof Node\Expr\AssignOp\Pow + || $expr instanceof Node\Expr\AssignOp\Mul + ) { + if ( + $this->resolveExpressionNodeToType($expr->var) instanceof Types\Integer + && $this->resolveExpressionNodeToType($expr->expr) instanceof Types\Integer + ) { + return new Types\Integer; + } + return new Types\Float_; + } + if ( $expr instanceof Node\Scalar\LNumber || $expr instanceof Node\Expr\Cast\Int_ - || $expr instanceof Node\Expr\Scalar\MagicConst\Line + || $expr instanceof Node\Scalar\MagicConst\Line || $expr instanceof Node\Expr\BinaryOp\Spaceship || $expr instanceof Node\Expr\BinaryOp\BitwiseAnd || $expr instanceof Node\Expr\BinaryOp\BitwiseOr @@ -606,7 +625,7 @@ class DefinitionResolver } if ( $expr instanceof Node\Expr\BinaryOp\Div - || $expr instanceof Node\Expr\DNumber + || $expr instanceof Node\Scalar\DNumber || $expr instanceof Node\Expr\Cast\Double ) { return new Types\Float_; @@ -702,7 +721,7 @@ class DefinitionResolver * Returns null if the node does not have a type. * * @param Node|string $node - * @return \phpDocumentor\Type|null + * @return \phpDocumentor\Reflection\Type|null */ public function getTypeFromNode($node) { @@ -730,6 +749,7 @@ class DefinitionResolver } } } + $type = null; if ($node->type !== null) { // Use PHP7 return type hint return $this->getTypeFromNode($node->type); @@ -792,7 +812,7 @@ class DefinitionResolver } } else if ($node instanceof Node\Const_) { return $this->resolveExpressionNodeToType($node->value); - } else if ($node instanceof Node\Expr\Assign || $node instanceof Node\Expr\AssignOp) { + } else { return $this->resolveExpressionNodeToType($node); } // TODO: read @property tags of class diff --git a/src/LanguageServer.php b/src/LanguageServer.php index 3c999f2..8fe9ec1 100644 --- a/src/LanguageServer.php +++ b/src/LanguageServer.php @@ -253,6 +253,7 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher } if ($this->workspace === null) { $this->workspace = new Server\Workspace( + $this->client, $this->projectIndex, $dependenciesIndex, $sourceIndex, diff --git a/src/NodeVisitor/NodeAtPositionFinder.php b/src/NodeVisitor/NodeAtPositionFinder.php index 521d940..cb17801 100644 --- a/src/NodeVisitor/NodeAtPositionFinder.php +++ b/src/NodeVisitor/NodeAtPositionFinder.php @@ -3,7 +3,7 @@ declare(strict_types = 1); namespace LanguageServer\NodeVisitor; -use PhpParser\{NodeVisitorAbstract, Node}; +use PhpParser\{NodeVisitorAbstract, Node, NodeTraverser}; use LanguageServer\Protocol\{Position, Range}; /** @@ -15,7 +15,7 @@ class NodeAtPositionFinder extends NodeVisitorAbstract /** * The node at the position, if found * - * @var Node + * @var Node|null */ public $node; @@ -34,9 +34,12 @@ class NodeAtPositionFinder extends NodeVisitorAbstract public function leaveNode(Node $node) { - $range = Range::fromNode($node); - if (!isset($this->node) && $range->includes($this->position)) { - $this->node = $node; + if ($this->node === null) { + $range = Range::fromNode($node); + if ($range->includes($this->position)) { + $this->node = $node; + return NodeTraverser::STOP_TRAVERSAL; + } } } } diff --git a/src/Protocol/FileEvent.php b/src/Protocol/FileEvent.php index b4ed833..015044d 100644 --- a/src/Protocol/FileEvent.php +++ b/src/Protocol/FileEvent.php @@ -20,4 +20,14 @@ class FileEvent * @var int */ public $type; + + /** + * @param string $uri + * @param int $type + */ + public function __construct(string $uri, int $type) + { + $this->uri = $uri; + $this->type = $type; + } } diff --git a/src/Protocol/SymbolInformation.php b/src/Protocol/SymbolInformation.php index 5d442f0..299dc55 100644 --- a/src/Protocol/SymbolInformation.php +++ b/src/Protocol/SymbolInformation.php @@ -60,6 +60,8 @@ class SymbolInformation $symbol->kind = SymbolKind::NAMESPACE; } else if ($node instanceof Node\Stmt\Function_) { $symbol->kind = SymbolKind::FUNCTION; + } else if ($node instanceof Node\Stmt\ClassMethod && ($node->name === '__construct' || $node->name === '__destruct')) { + $symbol->kind = SymbolKind::CONSTRUCTOR; } else if ($node instanceof Node\Stmt\ClassMethod) { $symbol->kind = SymbolKind::METHOD; } else if ($node instanceof Node\Stmt\PropertyProperty) { diff --git a/src/Server/Workspace.php b/src/Server/Workspace.php index b94618c..d2f8cec 100644 --- a/src/Server/Workspace.php +++ b/src/Server/Workspace.php @@ -5,7 +5,15 @@ namespace LanguageServer\Server; use LanguageServer\{LanguageClient, Project, PhpDocumentLoader}; use LanguageServer\Index\{ProjectIndex, DependenciesIndex, Index}; -use LanguageServer\Protocol\{SymbolInformation, SymbolDescriptor, ReferenceInformation, DependencyReference, Location}; +use LanguageServer\Protocol\{ + FileChangeType, + FileEvent, + SymbolInformation, + SymbolDescriptor, + ReferenceInformation, + DependencyReference, + Location +}; use Sabre\Event\Promise; use function Sabre\Event\coroutine; use function LanguageServer\{waitForEvent, getPackageName}; @@ -15,6 +23,11 @@ use function LanguageServer\{waitForEvent, getPackageName}; */ class Workspace { + /** + * @var LanguageClient + */ + public $client; + /** * The symbol index for the workspace * @@ -43,14 +56,16 @@ class Workspace public $documentLoader; /** + * @param LanguageClient $client LanguageClient instance used to signal updated results * @param ProjectIndex $index Index that is searched on a workspace/symbol request * @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(ProjectIndex $index, DependenciesIndex $dependenciesIndex, Index $sourceIndex, \stdClass $composerLock = null, PhpDocumentLoader $documentLoader, \stdClass $composerJson = null) + public function __construct(LanguageClient $client, ProjectIndex $index, DependenciesIndex $dependenciesIndex, Index $sourceIndex, \stdClass $composerLock = null, PhpDocumentLoader $documentLoader, \stdClass $composerJson = null) { + $this->client = $client; $this->sourceIndex = $sourceIndex; $this->index = $index; $this->dependenciesIndex = $dependenciesIndex; @@ -82,6 +97,21 @@ class Workspace }); } + /** + * The watched files notification is sent from the client to the server when the client detects changes to files watched by the language client. + * + * @param FileEvent[] $changes + * @return void + */ + public function didChangeWatchedFiles(array $changes) + { + foreach ($changes as $change) { + if ($change->type === FileChangeType::DELETED) { + $this->client->textDocument->publishDiagnostics($change->uri, []); + } + } + } + /** * The workspace references request is sent from the client to the server to locate project-wide references to a symbol given its description / metadata. * diff --git a/tests/LanguageServerTest.php b/tests/LanguageServerTest.php index 38b1fa1..c32a76a 100644 --- a/tests/LanguageServerTest.php +++ b/tests/LanguageServerTest.php @@ -57,7 +57,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 26 PHP files parsed') !== false) { $promise->fulfill(); } } @@ -103,7 +103,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 26 PHP files parsed') !== false) { $promise->fulfill(); } } diff --git a/tests/NodeVisitor/DefinitionCollectorTest.php b/tests/NodeVisitor/DefinitionCollectorTest.php index 3ee9995..7345f2c 100644 --- a/tests/NodeVisitor/DefinitionCollectorTest.php +++ b/tests/NodeVisitor/DefinitionCollectorTest.php @@ -50,7 +50,10 @@ class DefinitionCollectorTest extends TestCase 'TestNamespace\\TestTrait', 'TestNamespace\\TestInterface', 'TestNamespace\\test_function()', - 'TestNamespace\\ChildClass' + 'TestNamespace\\ChildClass', + 'TestNamespace\\Example', + 'TestNamespace\\Example->__construct()', + 'TestNamespace\\Example->__destruct()' ], array_keys($defNodes)); $this->assertInstanceOf(Node\Const_::class, $defNodes['TestNamespace\\TEST_CONST']); $this->assertInstanceOf(Node\Stmt\Class_::class, $defNodes['TestNamespace\\TestClass']); @@ -63,6 +66,9 @@ class DefinitionCollectorTest extends TestCase $this->assertInstanceOf(Node\Stmt\Interface_::class, $defNodes['TestNamespace\\TestInterface']); $this->assertInstanceOf(Node\Stmt\Function_::class, $defNodes['TestNamespace\\test_function()']); $this->assertInstanceOf(Node\Stmt\Class_::class, $defNodes['TestNamespace\\ChildClass']); + $this->assertInstanceOf(Node\Stmt\Class_::class, $defNodes['TestNamespace\\Example']); + $this->assertInstanceOf(Node\Stmt\ClassMethod::class, $defNodes['TestNamespace\\Example->__construct()']); + $this->assertInstanceOf(Node\Stmt\ClassMethod::class, $defNodes['TestNamespace\\Example->__destruct()']); } public function testDoesNotCollectReferences() diff --git a/tests/Server/ServerTestCase.php b/tests/Server/ServerTestCase.php index 679191f..f5fec55 100644 --- a/tests/Server/ServerTestCase.php +++ b/tests/Server/ServerTestCase.php @@ -54,7 +54,7 @@ abstract class ServerTestCase extends TestCase $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($projectIndex, $dependenciesIndex, $sourceIndex, null, $this->documentLoader); + $this->workspace = new Server\Workspace($client, $projectIndex, $dependenciesIndex, $sourceIndex, null, $this->documentLoader); $globalSymbolsUri = pathToUri(realpath(__DIR__ . '/../../fixtures/global_symbols.php')); $globalReferencesUri = pathToUri(realpath(__DIR__ . '/../../fixtures/global_references.php')); @@ -86,20 +86,23 @@ abstract class ServerTestCase extends TestCase 'whatever()' => new Location($globalReferencesUri, new Range(new Position(21, 0), new Position(23, 1))), // Namespaced - 'TestNamespace' => new Location($symbolsUri, new Range(new Position( 2, 10), new Position( 2, 23))), - 'SecondTestNamespace' => new Location($useUri, new Range(new Position( 2, 10), new Position( 2, 29))), - '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' => new Location($symbolsUri, new Range(new Position( 2, 10), new Position( 2, 23))), + 'SecondTestNamespace' => new Location($useUri, new Range(new Position( 2, 10), new Position( 2, 29))), + '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\\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))) ]; $this->referenceLocations = [ diff --git a/tests/Server/TextDocument/CompletionTest.php b/tests/Server/TextDocument/CompletionTest.php index 15349ed..29851e0 100644 --- a/tests/Server/TextDocument/CompletionTest.php +++ b/tests/Server/TextDocument/CompletionTest.php @@ -160,7 +160,12 @@ class CompletionTest extends TestCase 'TestClass', CompletionItemKind::CLASS_, null, - 'Pariatur ut laborum tempor voluptate consequat ea deserunt.', + 'Pariatur ut laborum tempor voluptate consequat ea deserunt.' . "\n\n" . + 'Deserunt enim minim sunt sint ea nisi. Deserunt excepteur tempor id nostrud' . "\n" . + 'laboris commodo ad commodo velit mollit qui non officia id. Nulla duis veniam' . "\n" . + 'veniam officia deserunt et non dolore mollit ea quis eiusmod sit non. Occaecat' . "\n" . + 'consequat sunt culpa exercitation pariatur id reprehenderit nisi incididunt Lorem' . "\n" . + 'sint. Officia culpa pariatur laborum nostrud cupidatat consequat mollit.', null, null, '\TestClass' @@ -179,7 +184,12 @@ class CompletionTest extends TestCase 'TestClass', CompletionItemKind::CLASS_, 'TestNamespace', - 'Pariatur ut laborum tempor voluptate consequat ea deserunt.', + 'Pariatur ut laborum tempor voluptate consequat ea deserunt.' . "\n\n" . + 'Deserunt enim minim sunt sint ea nisi. Deserunt excepteur tempor id nostrud' . "\n" . + 'laboris commodo ad commodo velit mollit qui non officia id. Nulla duis veniam' . "\n" . + 'veniam officia deserunt et non dolore mollit ea quis eiusmod sit non. Occaecat' . "\n" . + 'consequat sunt culpa exercitation pariatur id reprehenderit nisi incididunt Lorem' . "\n" . + 'sint. Officia culpa pariatur laborum nostrud cupidatat consequat mollit.', null, null, 'TestClass' @@ -193,6 +203,15 @@ class CompletionTest extends TestCase null, '\TestNamespace\ChildClass' ), + new CompletionItem( + 'Example', + CompletionItemKind::CLASS_, + 'TestNamespace', + null, + null, + null, + '\TestNamespace\Example' + ) ], true), $items); } @@ -209,7 +228,12 @@ class CompletionTest extends TestCase 'TestClass', CompletionItemKind::CLASS_, 'TestNamespace', - 'Pariatur ut laborum tempor voluptate consequat ea deserunt.' + 'Pariatur ut laborum tempor voluptate consequat ea deserunt.' . "\n\n" . + 'Deserunt enim minim sunt sint ea nisi. Deserunt excepteur tempor id nostrud' . "\n" . + 'laboris commodo ad commodo velit mollit qui non officia id. Nulla duis veniam' . "\n" . + 'veniam officia deserunt et non dolore mollit ea quis eiusmod sit non. Occaecat' . "\n" . + 'consequat sunt culpa exercitation pariatur id reprehenderit nisi incididunt Lorem' . "\n" . + 'sint. Officia culpa pariatur laborum nostrud cupidatat consequat mollit.' ) ], true), $items); } @@ -347,7 +371,12 @@ class CompletionTest extends TestCase 'TestClass', CompletionItemKind::CLASS_, null, - 'Pariatur ut laborum tempor voluptate consequat ea deserunt.', + 'Pariatur ut laborum tempor voluptate consequat ea deserunt.' . "\n\n" . + 'Deserunt enim minim sunt sint ea nisi. Deserunt excepteur tempor id nostrud' . "\n" . + 'laboris commodo ad commodo velit mollit qui non officia id. Nulla duis veniam' . "\n" . + 'veniam officia deserunt et non dolore mollit ea quis eiusmod sit non. Occaecat' . "\n" . + 'consequat sunt culpa exercitation pariatur id reprehenderit nisi incididunt Lorem' . "\n" . + 'sint. Officia culpa pariatur laborum nostrud cupidatat consequat mollit.', null, null, 'TestClass' @@ -433,4 +462,36 @@ class CompletionTest extends TestCase ) ], true), $items); } + + public function testBarePhp() + { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/bare_php.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(4, 8) + )->wait(); + $this->assertEquals(new CompletionList([ + new CompletionItem( + '$abc2', + CompletionItemKind::VARIABLE, + 'int', + null, + null, + null, + null, + new TextEdit(new Range(new Position(4, 8), new Position(4, 8)), 'c2') + ), + new CompletionItem( + '$abc', + CompletionItemKind::VARIABLE, + 'int', + null, + null, + null, + null, + new TextEdit(new Range(new Position(4, 8), new Position(4, 8)), 'c') + ) + ], true), $items); + } } diff --git a/tests/Server/TextDocument/DocumentSymbolTest.php b/tests/Server/TextDocument/DocumentSymbolTest.php index 89d24ee..155e4a2 100644 --- a/tests/Server/TextDocument/DocumentSymbolTest.php +++ b/tests/Server/TextDocument/DocumentSymbolTest.php @@ -18,18 +18,21 @@ class DocumentSymbolTest extends ServerTestCase $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('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') ], $result); // @codingStandardsIgnoreEnd } diff --git a/tests/Server/TextDocument/HoverTest.php b/tests/Server/TextDocument/HoverTest.php index 7d61354..cdc1718 100644 --- a/tests/Server/TextDocument/HoverTest.php +++ b/tests/Server/TextDocument/HoverTest.php @@ -22,7 +22,12 @@ class HoverTest extends ServerTestCase )->wait(); $this->assertEquals(new Hover([ new MarkedString('php', "range), $result); } @@ -37,7 +42,12 @@ class HoverTest extends ServerTestCase )->wait(); $this->assertEquals(new Hover([ new MarkedString('php', "range), $result); } @@ -181,7 +191,12 @@ class HoverTest extends ServerTestCase $result = $this->textDocument->hover(new TextDocumentIdentifier($uri), new Position(59, 11))->wait(); $this->assertEquals(new Hover([ new MarkedString('php', "on('message', function (Message $message) use ($fileEvent, &$isDiagnosticsCleared) { + if ($message->body->method === "textDocument/publishDiagnostics") { + $this->assertEquals($message->body->params->uri, $fileEvent->uri); + $this->assertEquals($message->body->params->diagnostics, []); + $isDiagnosticsCleared = true; + } + }); + + $workspace->didChangeWatchedFiles([$fileEvent]); + Loop\tick(true); + + $this->assertTrue($isDiagnosticsCleared, "Deleting file should clear all diagnostics."); + } +} diff --git a/tests/Server/Workspace/SymbolTest.php b/tests/Server/Workspace/SymbolTest.php index d3d13b6..5d0ed34 100644 --- a/tests/Server/Workspace/SymbolTest.php +++ b/tests/Server/Workspace/SymbolTest.php @@ -29,35 +29,38 @@ 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, 10), new Position(2, 23))), ''), + new SymbolInformation('TestNamespace', SymbolKind::NAMESPACE, new Location($referencesUri, new Range(new Position(2, 10), new Position(2, 23))), ''), // 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('whatever', SymbolKind::FUNCTION, $this->getDefinitionLocation('TestNamespace\\whatever()'), '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('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('whatever', SymbolKind::FUNCTION, $this->getDefinitionLocation('whatever()'), ''), + 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('whatever', SymbolKind::FUNCTION, $this->getDefinitionLocation('whatever()'), ''), - new SymbolInformation('SecondTestNamespace', SymbolKind::NAMESPACE, $this->getDefinitionLocation('SecondTestNamespace'), '') + new SymbolInformation('SecondTestNamespace', SymbolKind::NAMESPACE, $this->getDefinitionLocation('SecondTestNamespace'), '') ], $result); // @codingStandardsIgnoreEnd }