References (#52)
* Adds support for textDocument/references * Adds tests for global definitions and global fallbackpull/56/head
parent
66b5176a43
commit
6fe01183b0
|
@ -24,7 +24,7 @@
|
||||||
"bin": ["bin/php-language-server.php"],
|
"bin": ["bin/php-language-server.php"],
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=7.0",
|
"php": ">=7.0",
|
||||||
"nikic/php-parser": "^3.0.0beta1",
|
"nikic/php-parser": "dev-master#90834bff8eaf7b7f893253f312e73d8f532341ca",
|
||||||
"phpdocumentor/reflection-docblock": "^3.0",
|
"phpdocumentor/reflection-docblock": "^3.0",
|
||||||
"sabre/event": "^4.0",
|
"sabre/event": "^4.0",
|
||||||
"felixfbecker/advanced-json-rpc": "^1.2",
|
"felixfbecker/advanced-json-rpc": "^1.2",
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace GlobalFallback;
|
||||||
|
|
||||||
|
// Should fall back to global_symbols.php
|
||||||
|
test_function();
|
||||||
|
echo TEST_CONST;
|
||||||
|
|
||||||
|
// Should not fall back
|
||||||
|
$obj = new TestClass();
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$obj = new TestClass();
|
||||||
|
$obj->testMethod();
|
||||||
|
echo $obj->testProperty;
|
||||||
|
TestClass::staticTestMethod();
|
||||||
|
echo TestClass::$staticTestProperty;
|
||||||
|
echo TestClass::TEST_CLASS_CONST;
|
||||||
|
test_function();
|
||||||
|
|
||||||
|
$var = 123;
|
||||||
|
echo $var;
|
||||||
|
|
||||||
|
function whatever(TestClass $param): TestClass {
|
||||||
|
echo $param;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fn = function() use ($var) {
|
||||||
|
echo $var;
|
||||||
|
};
|
||||||
|
|
||||||
|
echo TEST_CONST;
|
|
@ -0,0 +1,53 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const TEST_CONST = 123;
|
||||||
|
|
||||||
|
class TestClass implements TestInterface
|
||||||
|
{
|
||||||
|
const TEST_CLASS_CONST = 123;
|
||||||
|
public static $staticTestProperty;
|
||||||
|
public $testProperty;
|
||||||
|
|
||||||
|
public static function staticTestMethod()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMethod($testParameter)
|
||||||
|
{
|
||||||
|
$testVariable = 123;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait TestTrait
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TestInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_function()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
new class {
|
||||||
|
const TEST_CLASS_CONST = 123;
|
||||||
|
public static $staticTestProperty;
|
||||||
|
public $testProperty;
|
||||||
|
|
||||||
|
public static function staticTestMethod()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMethod($testParameter)
|
||||||
|
{
|
||||||
|
$testVariable = 123;
|
||||||
|
}
|
||||||
|
};
|
|
@ -105,6 +105,8 @@ class LanguageServer extends \AdvancedJsonRpc\Dispatcher
|
||||||
$serverCapabilities->documentFormattingProvider = true;
|
$serverCapabilities->documentFormattingProvider = true;
|
||||||
// Support "Go to definition"
|
// Support "Go to definition"
|
||||||
$serverCapabilities->definitionProvider = true;
|
$serverCapabilities->definitionProvider = true;
|
||||||
|
// Support "Find all references"
|
||||||
|
$serverCapabilities->referencesProvider = true;
|
||||||
|
|
||||||
return new InitializeResult($serverCapabilities);
|
return new InitializeResult($serverCapabilities);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace LanguageServer\NodeVisitor;
|
||||||
|
|
||||||
|
use PhpParser\{NodeVisitorAbstract, Node};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects references to classes, interfaces, traits, methods, properties and constants
|
||||||
|
* Depends on ReferencesAdder and NameResolver
|
||||||
|
*/
|
||||||
|
class ReferencesCollector extends NodeVisitorAbstract
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Map from fully qualified name (FQN) to array of nodes that reference the symbol
|
||||||
|
*
|
||||||
|
* @var Node[][]
|
||||||
|
*/
|
||||||
|
public $references = [];
|
||||||
|
|
||||||
|
public function enterNode(Node $node)
|
||||||
|
{
|
||||||
|
// Check if the node references any global symbol
|
||||||
|
$fqn = $node->getAttribute('ownerDocument')->getReferencedFqn($node);
|
||||||
|
if ($fqn) {
|
||||||
|
$this->addReference($fqn, $node);
|
||||||
|
// Namespaced constant access and function calls also need to register a reference
|
||||||
|
// to the global version because PHP falls back to global at runtime
|
||||||
|
// http://php.net/manual/en/language.namespaces.fallback.php
|
||||||
|
$parent = $node->getAttribute('parentNode');
|
||||||
|
if ($parent instanceof Node\Expr\ConstFetch || $parent instanceof Node\Expr\FuncCall) {
|
||||||
|
$parts = explode('\\', $fqn);
|
||||||
|
if (count($parts) > 1) {
|
||||||
|
$globalFqn = end($parts);
|
||||||
|
$this->addReference($globalFqn, $node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Namespaced constant references and function calls also need to register a reference to the global
|
||||||
|
// Static method calls, constant and property fetches also need to register a reference to the class
|
||||||
|
// A reference like TestNamespace\TestClass::myStaticMethod() registers a reference for
|
||||||
|
// - TestNamespace\TestClass
|
||||||
|
// - TestNamespace\TestClass::myStaticMethod()
|
||||||
|
if (
|
||||||
|
($node instanceof Node\Expr\StaticCall
|
||||||
|
|| $node instanceof Node\Expr\StaticPropertyFetch
|
||||||
|
|| $node instanceof Node\Expr\ClassConstFetch)
|
||||||
|
&& $node->class instanceof Node\Name
|
||||||
|
) {
|
||||||
|
$this->addReference((string)$node->class, $node->class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addReference(string $fqn, Node $node)
|
||||||
|
{
|
||||||
|
if (!isset($this->references[$fqn])) {
|
||||||
|
$this->references[$fqn] = [];
|
||||||
|
}
|
||||||
|
$this->references[$fqn][] = $node;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace LanguageServer\NodeVisitor;
|
||||||
|
|
||||||
|
use PhpParser\{NodeVisitorAbstract, Node, NodeTraverser};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects all references to a variable
|
||||||
|
*/
|
||||||
|
class VariableReferencesCollector extends NodeVisitorAbstract
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Array of references to the variable
|
||||||
|
*
|
||||||
|
* @var Node\Expr\Variable[]
|
||||||
|
*/
|
||||||
|
public $references = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name The variable name
|
||||||
|
*/
|
||||||
|
public function __construct(string $name)
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function enterNode(Node $node)
|
||||||
|
{
|
||||||
|
if ($node instanceof Node\Expr\Variable && $node->name === $this->name) {
|
||||||
|
$this->references[] = $node;
|
||||||
|
} else if ($node instanceof Node\FunctionLike) {
|
||||||
|
// If we meet a function node, dont traverse its statements, they are in another scope
|
||||||
|
// except it is a closure that has imported the variable through use
|
||||||
|
if ($node instanceof Node\Expr\Closure) {
|
||||||
|
foreach ($node->uses as $use) {
|
||||||
|
if ($use->var === $this->name) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NodeTraverser::DONT_TRAVERSE_CHILDREN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,14 @@ declare(strict_types = 1);
|
||||||
namespace LanguageServer;
|
namespace LanguageServer;
|
||||||
|
|
||||||
use LanguageServer\Protocol\{Diagnostic, DiagnosticSeverity, Range, Position, TextEdit};
|
use LanguageServer\Protocol\{Diagnostic, DiagnosticSeverity, Range, Position, TextEdit};
|
||||||
use LanguageServer\NodeVisitor\{NodeAtPositionFinder, ReferencesAdder, DefinitionCollector, ColumnCalculator};
|
use LanguageServer\NodeVisitor\{
|
||||||
|
NodeAtPositionFinder,
|
||||||
|
ReferencesAdder,
|
||||||
|
DefinitionCollector,
|
||||||
|
ColumnCalculator,
|
||||||
|
ReferencesCollector,
|
||||||
|
VariableReferencesCollector
|
||||||
|
};
|
||||||
use PhpParser\{Error, Node, NodeTraverser, Parser};
|
use PhpParser\{Error, Node, NodeTraverser, Parser};
|
||||||
use PhpParser\NodeVisitor\NameResolver;
|
use PhpParser\NodeVisitor\NameResolver;
|
||||||
|
|
||||||
|
@ -52,7 +59,7 @@ class PhpDocument
|
||||||
*
|
*
|
||||||
* @var Node[]
|
* @var Node[]
|
||||||
*/
|
*/
|
||||||
private $statements;
|
private $stmts;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map from fully qualified name (FQN) to Node
|
* Map from fully qualified name (FQN) to Node
|
||||||
|
@ -85,6 +92,18 @@ class PhpDocument
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Get all references of a fully qualified name
|
||||||
|
*
|
||||||
|
* @param string $fqn The fully qualified name of the symbol
|
||||||
|
* @return Node[]
|
||||||
|
*/
|
||||||
|
public function getReferencesByFqn(string $fqn)
|
||||||
|
{
|
||||||
|
return isset($this->references) && isset($this->references[$fqn]) ? $this->references[$fqn] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the content on this document.
|
||||||
* Re-parses a source file, updates symbols and reports parsing errors
|
* Re-parses a source file, updates symbols and reports parsing errors
|
||||||
* that may have occured as diagnostics.
|
* that may have occured as diagnostics.
|
||||||
*
|
*
|
||||||
|
@ -135,19 +154,32 @@ class PhpDocument
|
||||||
// Add column attributes to nodes
|
// Add column attributes to nodes
|
||||||
$traverser->addVisitor(new ColumnCalculator($content));
|
$traverser->addVisitor(new ColumnCalculator($content));
|
||||||
|
|
||||||
|
$traverser->traverse($stmts);
|
||||||
|
$traverser = new NodeTraverser;
|
||||||
|
|
||||||
// Collect all definitions
|
// Collect all definitions
|
||||||
$definitionCollector = new DefinitionCollector;
|
$definitionCollector = new DefinitionCollector;
|
||||||
$traverser->addVisitor($definitionCollector);
|
$traverser->addVisitor($definitionCollector);
|
||||||
|
|
||||||
|
// Collect all references
|
||||||
|
$referencesCollector = new ReferencesCollector($this->definitions);
|
||||||
|
$traverser->addVisitor($referencesCollector);
|
||||||
|
|
||||||
$traverser->traverse($stmts);
|
$traverser->traverse($stmts);
|
||||||
|
|
||||||
// Register this document on the project for all the symbols defined in it
|
// Register this document on the project for all the symbols defined in it
|
||||||
|
$this->definitions = $definitionCollector->definitions;
|
||||||
foreach ($definitionCollector->definitions as $fqn => $node) {
|
foreach ($definitionCollector->definitions as $fqn => $node) {
|
||||||
$this->project->setDefinitionUri($fqn, $this->uri);
|
$this->project->setDefinitionUri($fqn, $this->uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->statements = $stmts;
|
// Register this document on the project for references
|
||||||
$this->definitions = $definitionCollector->definitions;
|
$this->references = $referencesCollector->references;
|
||||||
|
foreach ($referencesCollector->references as $fqn => $nodes) {
|
||||||
|
$this->project->addReferenceUri($fqn, $this->uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->stmts = $stmts;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,6 +216,16 @@ class PhpDocument
|
||||||
return $this->uri;
|
return $this->uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the AST of the document
|
||||||
|
*
|
||||||
|
* @return Node[]
|
||||||
|
*/
|
||||||
|
public function getStmts(): array
|
||||||
|
{
|
||||||
|
return $this->stmts;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the node at a specified position
|
* Returns the node at a specified position
|
||||||
*
|
*
|
||||||
|
@ -195,7 +237,7 @@ class PhpDocument
|
||||||
$traverser = new NodeTraverser;
|
$traverser = new NodeTraverser;
|
||||||
$finder = new NodeAtPositionFinder($position);
|
$finder = new NodeAtPositionFinder($position);
|
||||||
$traverser->addVisitor($finder);
|
$traverser->addVisitor($finder);
|
||||||
$traverser->traverse($this->statements);
|
$traverser->traverse($this->stmts);
|
||||||
return $finder->node;
|
return $finder->node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,33 +337,31 @@ class PhpDocument
|
||||||
*/
|
*/
|
||||||
public function getReferencedFqn(Node $node)
|
public function getReferencedFqn(Node $node)
|
||||||
{
|
{
|
||||||
if ($node instanceof Node\Name) {
|
$parent = $node->getAttribute('parentNode');
|
||||||
$nameNode = $node;
|
|
||||||
$node = $node->getAttribute('parentNode');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
($node instanceof Node\Stmt\ClassLike
|
$node instanceof Node\Name && (
|
||||||
|| $node instanceof Node\Param
|
$parent instanceof Node\Stmt\ClassLike
|
||||||
|| $node instanceof Node\Stmt\Function_)
|
|| $parent instanceof Node\Param
|
||||||
&& isset($nameNode)
|
|| $parent instanceof Node\Stmt\Function_
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
// For extends, implements and type hints use the name directly
|
// For extends, implements and type hints use the name directly
|
||||||
$name = (string)$nameNode;
|
$name = (string)$node;
|
||||||
// Only the name node should be considered a reference, not the UseUse node itself
|
// Only the name node should be considered a reference, not the UseUse node itself
|
||||||
} else if ($node instanceof Node\Stmt\UseUse && isset($nameNode)) {
|
} else if ($parent instanceof Node\Stmt\UseUse) {
|
||||||
$name = (string)$node->name;
|
$name = (string)$parent->name;
|
||||||
$parent = $node->getAttribute('parentNode');
|
$grandParent = $parent->getAttribute('parentNode');
|
||||||
if ($parent instanceof Node\Stmt\GroupUse) {
|
if ($grandParent instanceof Node\Stmt\GroupUse) {
|
||||||
$name = $parent->prefix . '\\' . $name;
|
$name = $grandParent->prefix . '\\' . $name;
|
||||||
}
|
}
|
||||||
// Only the name node should be considered a reference, not the New_ node itself
|
// Only the name node should be considered a reference, not the New_ node itself
|
||||||
} else if ($node instanceof Node\Expr\New_ && isset($nameNode)) {
|
} else if ($parent instanceof Node\Expr\New_) {
|
||||||
if (!($node->class instanceof Node\Name)) {
|
if (!($parent->class instanceof Node\Name)) {
|
||||||
// Cannot get definition of dynamic calls
|
// Cannot get definition of dynamic calls
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
$name = (string)$node->class;
|
$name = (string)$parent->class;
|
||||||
} else if ($node instanceof Node\Expr\MethodCall || $node instanceof Node\Expr\PropertyFetch) {
|
} else if ($node instanceof Node\Expr\MethodCall || $node instanceof Node\Expr\PropertyFetch) {
|
||||||
if ($node->name instanceof Node\Expr || !($node->var instanceof Node\Expr\Variable)) {
|
if ($node->name instanceof Node\Expr || !($node->var instanceof Node\Expr\Variable)) {
|
||||||
// Cannot get definition of dynamic calls
|
// Cannot get definition of dynamic calls
|
||||||
|
@ -353,13 +393,13 @@ class PhpDocument
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
$name .= '::' . (string)$node->name;
|
$name .= '::' . (string)$node->name;
|
||||||
} else if ($node instanceof Node\Expr\FuncCall) {
|
} else if ($parent instanceof Node\Expr\FuncCall) {
|
||||||
if ($node->name instanceof Node\Expr) {
|
if ($parent->name instanceof Node\Expr) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
$name = (string)$node->name;
|
$name = (string)($node->getAttribute('namespacedName') ?? $parent->name);
|
||||||
} else if ($node instanceof Node\Expr\ConstFetch) {
|
} else if ($parent instanceof Node\Expr\ConstFetch) {
|
||||||
$name = (string)$node->name;
|
$name = (string)($node->getAttribute('namespacedName') ?? $parent->name);
|
||||||
} else if (
|
} else if (
|
||||||
$node instanceof Node\Expr\ClassConstFetch
|
$node instanceof Node\Expr\ClassConstFetch
|
||||||
|| $node instanceof Node\Expr\StaticPropertyFetch
|
|| $node instanceof Node\Expr\StaticPropertyFetch
|
||||||
|
@ -370,35 +410,19 @@ class PhpDocument
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
$name = (string)$node->class . '::' . $node->name;
|
$name = (string)$node->class . '::' . $node->name;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
$node instanceof Node\Expr\MethodCall
|
$node instanceof Node\Expr\MethodCall
|
||||||
|| $node instanceof Node\Expr\FuncCall
|
|
||||||
|| $node instanceof Node\Expr\StaticCall
|
|| $node instanceof Node\Expr\StaticCall
|
||||||
|
|| $parent instanceof Node\Expr\FuncCall
|
||||||
) {
|
) {
|
||||||
$name .= '()';
|
$name .= '()';
|
||||||
}
|
}
|
||||||
if (!isset($name)) {
|
if (!isset($name)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// If the node is a function or constant, it could be namespaced, but PHP falls back to global
|
|
||||||
// The NameResolver therefor does not resolve these to namespaced names
|
|
||||||
// http://php.net/manual/en/language.namespaces.fallback.php
|
|
||||||
if ($node instanceof Node\Expr\FuncCall || $node instanceof Node\Expr\ConstFetch) {
|
|
||||||
// Find and try with namespace
|
|
||||||
$n = $node;
|
|
||||||
while (isset($n)) {
|
|
||||||
$n = $n->getAttribute('parentNode');
|
|
||||||
if ($n instanceof Node\Stmt\Namespace_) {
|
|
||||||
$namespacedName = (string)$n->name . '\\' . $name;
|
|
||||||
// If the namespaced version is defined, return that
|
|
||||||
// Otherwise fall back to global
|
|
||||||
if ($this->project->isDefined($namespacedName)) {
|
|
||||||
return $namespacedName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $name;
|
return $name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -421,12 +445,69 @@ class PhpDocument
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
$document = $this->project->getDefinitionDocument($fqn);
|
$document = $this->project->getDefinitionDocument($fqn);
|
||||||
|
if (!isset($document)) {
|
||||||
|
// If the node is a function or constant, it could be namespaced, but PHP falls back to global
|
||||||
|
// http://php.net/manual/en/language.namespaces.fallback.php
|
||||||
|
$parent = $node->getAttribute('parentNode');
|
||||||
|
if ($parent instanceof Node\Expr\ConstFetch || $parent instanceof Node\Expr\FuncCall) {
|
||||||
|
$parts = explode('\\', $fqn);
|
||||||
|
$fqn = end($parts);
|
||||||
|
$document = $this->project->getDefinitionDocument($fqn);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!isset($document)) {
|
if (!isset($document)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return $document->getDefinitionByFqn($fqn);
|
return $document->getDefinitionByFqn($fqn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the reference nodes for any node
|
||||||
|
* The references node MAY be in other documents, check the ownerDocument attribute
|
||||||
|
*
|
||||||
|
* @param Node $node
|
||||||
|
* @return Node[]
|
||||||
|
*/
|
||||||
|
public function getReferencesByNode(Node $node)
|
||||||
|
{
|
||||||
|
// Variables always stay in the boundary of the file and need to be searched inside their function scope
|
||||||
|
// by traversing the AST
|
||||||
|
if ($node instanceof Node\Expr\Variable || $node instanceof Node\Param) {
|
||||||
|
if ($node->name instanceof Node\Expr) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Find function/method/closure scope
|
||||||
|
$n = $node;
|
||||||
|
while (isset($n) && !($n instanceof Node\FunctionLike)) {
|
||||||
|
$n = $n->getAttribute('parentNode');
|
||||||
|
}
|
||||||
|
if (!isset($n)) {
|
||||||
|
$n = $node->getAttribute('ownerDocument');
|
||||||
|
}
|
||||||
|
$traverser = new NodeTraverser;
|
||||||
|
$refCollector = new VariableReferencesCollector($node->name);
|
||||||
|
$traverser->addVisitor($refCollector);
|
||||||
|
$traverser->traverse($n->getStmts());
|
||||||
|
return $refCollector->references;
|
||||||
|
}
|
||||||
|
// Definition with a global FQN
|
||||||
|
$fqn = $this->getDefinedFqn($node);
|
||||||
|
if ($fqn === null) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
$refDocuments = $this->project->getReferenceDocuments($fqn);
|
||||||
|
$nodes = [];
|
||||||
|
foreach ($refDocuments as $document) {
|
||||||
|
$refs = $document->getReferencesByFqn($fqn);
|
||||||
|
if ($refs !== null) {
|
||||||
|
foreach ($refs as $ref) {
|
||||||
|
$nodes[] = $ref;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $nodes;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the assignment or parameter node where a variable was defined
|
* Returns the assignment or parameter node where a variable was defined
|
||||||
*
|
*
|
||||||
|
@ -437,7 +518,7 @@ class PhpDocument
|
||||||
{
|
{
|
||||||
$n = $var;
|
$n = $var;
|
||||||
// Traverse the AST up
|
// Traverse the AST up
|
||||||
while (isset($n) && $n = $n->getAttribute('parentNode')) {
|
do {
|
||||||
// If a function is met, check the parameters and use statements
|
// If a function is met, check the parameters and use statements
|
||||||
if ($n instanceof Node\FunctionLike) {
|
if ($n instanceof Node\FunctionLike) {
|
||||||
foreach ($n->getParams() as $param) {
|
foreach ($n->getParams() as $param) {
|
||||||
|
@ -461,7 +542,7 @@ class PhpDocument
|
||||||
return $n;
|
return $n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} while (isset($n) && $n = $n->getAttribute('parentNode'));
|
||||||
// Return null if nothing was found
|
// Return null if nothing was found
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,12 +16,19 @@ class Project
|
||||||
private $documents = [];
|
private $documents = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An associative array that maps fully qualified symbol names to document URIs
|
* An associative array that maps fully qualified symbol names to document URIs that define the symbol
|
||||||
*
|
*
|
||||||
* @var string[]
|
* @var string[]
|
||||||
*/
|
*/
|
||||||
private $definitions = [];
|
private $definitions = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An associative array that maps fully qualified symbol names to arrays of document URIs that reference the symbol
|
||||||
|
*
|
||||||
|
* @var PhpDocument[][]
|
||||||
|
*/
|
||||||
|
private $references = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instance of the PHP parser
|
* Instance of the PHP parser
|
||||||
*
|
*
|
||||||
|
@ -143,6 +150,37 @@ class Project
|
||||||
$this->definitions[$fqn] = $uri;
|
$this->definitions[$fqn] = $uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a document URI as a referencee of a specific symbol
|
||||||
|
*
|
||||||
|
* @param string $fqn The fully qualified name of the symbol
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addReferenceUri(string $fqn, string $uri)
|
||||||
|
{
|
||||||
|
if (!isset($this->references[$fqn])) {
|
||||||
|
$this->references[$fqn] = [];
|
||||||
|
}
|
||||||
|
// TODO: use DS\Set instead of searching array
|
||||||
|
if (array_search($uri, $this->references[$fqn], true) === false) {
|
||||||
|
$this->references[$fqn][] = $uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all documents that reference a symbol
|
||||||
|
*
|
||||||
|
* @param string $fqn The fully qualified name of the symbol
|
||||||
|
* @return PhpDocument[]
|
||||||
|
*/
|
||||||
|
public function getReferenceDocuments(string $fqn)
|
||||||
|
{
|
||||||
|
if (!isset($this->references[$fqn])) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return array_map([$this, 'getDocument'], $this->references[$fqn]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the document where a symbol is defined
|
* Returns the document where a symbol is defined
|
||||||
*
|
*
|
||||||
|
|
|
@ -12,7 +12,8 @@ use LanguageServer\Protocol\{
|
||||||
FormattingOptions,
|
FormattingOptions,
|
||||||
TextEdit,
|
TextEdit,
|
||||||
Location,
|
Location,
|
||||||
SymbolInformation
|
SymbolInformation,
|
||||||
|
ReferenceContext
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -104,6 +105,28 @@ class TextDocument
|
||||||
return $this->project->getDocument($textDocument->uri)->getFormattedText();
|
return $this->project->getDocument($textDocument->uri)->getFormattedText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The references request is sent from the client to the server to resolve project-wide references for the symbol
|
||||||
|
* denoted by the given text document position.
|
||||||
|
*
|
||||||
|
* @param ReferenceContext $context
|
||||||
|
* @return Location[]
|
||||||
|
*/
|
||||||
|
public function references(ReferenceContext $context, TextDocumentIdentifier $textDocument, Position $position): array
|
||||||
|
{
|
||||||
|
$document = $this->project->getDocument($textDocument->uri);
|
||||||
|
$node = $document->getNodeAtPosition($position);
|
||||||
|
if ($node === null) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
$refs = $document->getReferencesByNode($node);
|
||||||
|
$locations = [];
|
||||||
|
foreach ($refs as $ref) {
|
||||||
|
$locations[] = Location::fromNode($ref);
|
||||||
|
}
|
||||||
|
return $locations;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The goto definition request is sent from the client to the server to resolve the definition location of a symbol
|
* The goto definition request is sent from the client to the server to resolve the definition location of a symbol
|
||||||
* at a given text document position.
|
* at a given text document position.
|
||||||
|
|
|
@ -35,7 +35,7 @@ class LanguageServerTest extends TestCase
|
||||||
'completionProvider' => null,
|
'completionProvider' => null,
|
||||||
'signatureHelpProvider' => null,
|
'signatureHelpProvider' => null,
|
||||||
'definitionProvider' => true,
|
'definitionProvider' => true,
|
||||||
'referencesProvider' => null,
|
'referencesProvider' => true,
|
||||||
'documentHighlightProvider' => null,
|
'documentHighlightProvider' => null,
|
||||||
'workspaceSymbolProvider' => true,
|
'workspaceSymbolProvider' => true,
|
||||||
'codeActionProvider' => null,
|
'codeActionProvider' => null,
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace LanguageServer\Tests\Server\TextDocument\Definition;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use LanguageServer\Tests\MockProtocolStream;
|
||||||
|
use LanguageServer\{Server, LanguageClient, Project};
|
||||||
|
use LanguageServer\Protocol\{TextDocumentIdentifier, Position};
|
||||||
|
|
||||||
|
class GlobalFallbackTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Server\TextDocument
|
||||||
|
*/
|
||||||
|
private $textDocument;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$client = new LanguageClient(new MockProtocolStream());
|
||||||
|
$project = new Project($client);
|
||||||
|
$this->textDocument = new Server\TextDocument($project, $client);
|
||||||
|
$project->openDocument('global_fallback', file_get_contents(__DIR__ . '/../../../../fixtures/global_fallback.php'));
|
||||||
|
$project->openDocument('global_symbols', file_get_contents(__DIR__ . '/../../../../fixtures/global_symbols.php'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClassDoesNotFallback()
|
||||||
|
{
|
||||||
|
// $obj = new TestClass();
|
||||||
|
// Get definition for TestClass should not fall back to global
|
||||||
|
$result = $this->textDocument->definition(new TextDocumentIdentifier('global_fallback'), new Position(9, 16));
|
||||||
|
$this->assertEquals([], $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFallsBackForConstants()
|
||||||
|
{
|
||||||
|
// echo TEST_CONST;
|
||||||
|
// Get definition for TEST_CONST
|
||||||
|
$result = $this->textDocument->definition(new TextDocumentIdentifier('global_fallback'), new Position(6, 10));
|
||||||
|
$this->assertEquals([
|
||||||
|
'uri' => 'global_symbols',
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 4,
|
||||||
|
'character' => 6
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 4,
|
||||||
|
'character' => 22
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFallsBackForFunctions()
|
||||||
|
{
|
||||||
|
// test_function();
|
||||||
|
// Get definition for test_function
|
||||||
|
$result = $this->textDocument->definition(new TextDocumentIdentifier('global_fallback'), new Position(5, 6));
|
||||||
|
$this->assertEquals([
|
||||||
|
'uri' => 'global_symbols',
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 33,
|
||||||
|
'character' => 0
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 36,
|
||||||
|
'character' => 1
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,321 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace LanguageServer\Tests\Server\TextDocument\Definition;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use LanguageServer\Tests\MockProtocolStream;
|
||||||
|
use LanguageServer\{Server, LanguageClient, Project};
|
||||||
|
use LanguageServer\Protocol\{TextDocumentIdentifier, Position};
|
||||||
|
|
||||||
|
class GlobalTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Server\TextDocument
|
||||||
|
*/
|
||||||
|
private $textDocument;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$client = new LanguageClient(new MockProtocolStream());
|
||||||
|
$project = new Project($client);
|
||||||
|
$this->textDocument = new Server\TextDocument($project, $client);
|
||||||
|
$project->openDocument('references', file_get_contents(__DIR__ . '/../../../../fixtures/global_references.php'));
|
||||||
|
$project->openDocument('symbols', file_get_contents(__DIR__ . '/../../../../fixtures/global_symbols.php'));
|
||||||
|
// Load this to check that there are no conflicts
|
||||||
|
$project->openDocument('references_namespaced', file_get_contents(__DIR__ . '/../../../../fixtures/references.php'));
|
||||||
|
$project->openDocument('symbols_namespaced', file_get_contents(__DIR__ . '/../../../../fixtures/symbols.php'));
|
||||||
|
$project->openDocument('use', file_get_contents(__DIR__ . '/../../../../fixtures/use.php'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDefinitionFileBeginning() {
|
||||||
|
// |<?php
|
||||||
|
$result = $this->textDocument->definition(new TextDocumentIdentifier('references'), new Position(0, 0));
|
||||||
|
$this->assertEquals([], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDefinitionEmptyResult() {
|
||||||
|
// namespace keyword
|
||||||
|
$result = $this->textDocument->definition(new TextDocumentIdentifier('references'), new Position(2, 4));
|
||||||
|
$this->assertEquals([], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDefinitionForClassLike()
|
||||||
|
{
|
||||||
|
// $obj = new TestClass();
|
||||||
|
// Get definition for TestClass
|
||||||
|
$result = $this->textDocument->definition(new TextDocumentIdentifier('references'), new Position(4, 16));
|
||||||
|
$this->assertEquals([
|
||||||
|
'uri' => 'symbols',
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 6,
|
||||||
|
'character' => 0
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 21,
|
||||||
|
'character' => 1
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDefinitionForImplements()
|
||||||
|
{
|
||||||
|
// class TestClass implements TestInterface
|
||||||
|
// Get definition for TestInterface
|
||||||
|
$result = $this->textDocument->definition(new TextDocumentIdentifier('symbols'), new Position(6, 33));
|
||||||
|
$this->assertEquals([
|
||||||
|
'uri' => 'symbols',
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 28,
|
||||||
|
'character' => 0
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 31,
|
||||||
|
'character' => 1
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDefinitionForClassConstants()
|
||||||
|
{
|
||||||
|
// echo TestClass::TEST_CLASS_CONST;
|
||||||
|
// Get definition for TEST_CLASS_CONST
|
||||||
|
$result = $this->textDocument->definition(new TextDocumentIdentifier('references'), new Position(9, 21));
|
||||||
|
$this->assertEquals([
|
||||||
|
'uri' => 'symbols',
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 8,
|
||||||
|
'character' => 10
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 8,
|
||||||
|
'character' => 32
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDefinitionForConstants()
|
||||||
|
{
|
||||||
|
// echo TEST_CONST;
|
||||||
|
// Get definition for TEST_CONST
|
||||||
|
$result = $this->textDocument->definition(new TextDocumentIdentifier('references'), new Position(23, 9));
|
||||||
|
$this->assertEquals([
|
||||||
|
'uri' => 'symbols',
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 4,
|
||||||
|
'character' => 6
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 4,
|
||||||
|
'character' => 22
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDefinitionForStaticMethods()
|
||||||
|
{
|
||||||
|
// TestClass::staticTestMethod();
|
||||||
|
// Get definition for staticTestMethod
|
||||||
|
$result = $this->textDocument->definition(new TextDocumentIdentifier('references'), new Position(7, 20));
|
||||||
|
$this->assertEquals([
|
||||||
|
'uri' => 'symbols',
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 12,
|
||||||
|
'character' => 4
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 15,
|
||||||
|
'character' => 5
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDefinitionForStaticProperties()
|
||||||
|
{
|
||||||
|
// echo TestClass::$staticTestProperty;
|
||||||
|
// Get definition for staticTestProperty
|
||||||
|
$result = $this->textDocument->definition(new TextDocumentIdentifier('references'), new Position(8, 25));
|
||||||
|
$this->assertEquals([
|
||||||
|
'uri' => 'symbols',
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 9,
|
||||||
|
'character' => 18
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 9,
|
||||||
|
'character' => 37
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDefinitionForMethods()
|
||||||
|
{
|
||||||
|
// $obj->testMethod();
|
||||||
|
// Get definition for testMethod
|
||||||
|
$result = $this->textDocument->definition(new TextDocumentIdentifier('references'), new Position(5, 11));
|
||||||
|
$this->assertEquals([
|
||||||
|
'uri' => 'symbols',
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 17,
|
||||||
|
'character' => 4
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 20,
|
||||||
|
'character' => 5
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDefinitionForProperties()
|
||||||
|
{
|
||||||
|
// echo $obj->testProperty;
|
||||||
|
// Get definition for testProperty
|
||||||
|
$result = $this->textDocument->definition(new TextDocumentIdentifier('references'), new Position(6, 18));
|
||||||
|
$this->assertEquals([
|
||||||
|
'uri' => 'symbols',
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 10,
|
||||||
|
'character' => 11
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 10,
|
||||||
|
'character' => 24
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDefinitionForVariables()
|
||||||
|
{
|
||||||
|
// echo $var;
|
||||||
|
// Get definition for $var
|
||||||
|
$result = $this->textDocument->definition(new TextDocumentIdentifier('references'), new Position(13, 7));
|
||||||
|
$this->assertEquals([
|
||||||
|
'uri' => 'references',
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 12,
|
||||||
|
'character' => 0
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 12,
|
||||||
|
'character' => 10
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDefinitionForParamTypeHints()
|
||||||
|
{
|
||||||
|
// function whatever(TestClass $param) {
|
||||||
|
// Get definition for TestClass
|
||||||
|
$result = $this->textDocument->definition(new TextDocumentIdentifier('references'), new Position(15, 23));
|
||||||
|
$this->assertEquals([
|
||||||
|
'uri' => 'symbols',
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 6,
|
||||||
|
'character' => 0
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 21,
|
||||||
|
'character' => 1
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
public function testDefinitionForReturnTypeHints()
|
||||||
|
{
|
||||||
|
// function whatever(TestClass $param) {
|
||||||
|
// Get definition for TestClass
|
||||||
|
$result = $this->textDocument->definition(new TextDocumentIdentifier('references'), new Position(15, 42));
|
||||||
|
$this->assertEquals([
|
||||||
|
'uri' => 'symbols',
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 6,
|
||||||
|
'character' => 0
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 21,
|
||||||
|
'character' => 1
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDefinitionForParams()
|
||||||
|
{
|
||||||
|
// echo $param;
|
||||||
|
// Get definition for $param
|
||||||
|
$result = $this->textDocument->definition(new TextDocumentIdentifier('references'), new Position(16, 13));
|
||||||
|
$this->assertEquals([
|
||||||
|
'uri' => 'references',
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 15,
|
||||||
|
'character' => 18
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 15,
|
||||||
|
'character' => 34
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDefinitionForUsedVariables()
|
||||||
|
{
|
||||||
|
// echo $var;
|
||||||
|
// Get definition for $var
|
||||||
|
$result = $this->textDocument->definition(new TextDocumentIdentifier('references'), new Position(20, 11));
|
||||||
|
$this->assertEquals([
|
||||||
|
'uri' => 'references',
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 19,
|
||||||
|
'character' => 22
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 19,
|
||||||
|
'character' => 26
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDefinitionForFunctions()
|
||||||
|
{
|
||||||
|
// test_function();
|
||||||
|
// Get definition for test_function
|
||||||
|
$result = $this->textDocument->definition(new TextDocumentIdentifier('references'), new Position(10, 4));
|
||||||
|
$this->assertEquals([
|
||||||
|
'uri' => 'symbols',
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 33,
|
||||||
|
'character' => 0
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 36,
|
||||||
|
'character' => 1
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,15 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types = 1);
|
declare(strict_types = 1);
|
||||||
|
|
||||||
namespace LanguageServer\Tests\Server\TextDocument;
|
namespace LanguageServer\Tests\Server\TextDocument\Definition;
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use LanguageServer\Tests\MockProtocolStream;
|
use LanguageServer\Tests\MockProtocolStream;
|
||||||
use LanguageServer\{Server, LanguageClient, Project};
|
use LanguageServer\{Server, LanguageClient, Project};
|
||||||
use LanguageServer\Protocol\{TextDocumentIdentifier, Position};
|
use LanguageServer\Protocol\{TextDocumentIdentifier, Position};
|
||||||
|
use function LanguageServer\pathToUri;
|
||||||
|
|
||||||
class DefinitionTest extends TestCase
|
class NamespacedTest extends TestCase
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var Server\TextDocument
|
* @var Server\TextDocument
|
||||||
|
@ -20,9 +21,9 @@ class DefinitionTest extends TestCase
|
||||||
$client = new LanguageClient(new MockProtocolStream());
|
$client = new LanguageClient(new MockProtocolStream());
|
||||||
$project = new Project($client);
|
$project = new Project($client);
|
||||||
$this->textDocument = new Server\TextDocument($project, $client);
|
$this->textDocument = new Server\TextDocument($project, $client);
|
||||||
$project->openDocument('references', file_get_contents(__DIR__ . '/../../../fixtures/references.php'));
|
$project->openDocument('references', file_get_contents(__DIR__ . '/../../../../fixtures/references.php'));
|
||||||
$project->openDocument('symbols', file_get_contents(__DIR__ . '/../../../fixtures/symbols.php'));
|
$project->openDocument('symbols', file_get_contents(__DIR__ . '/../../../../fixtures/symbols.php'));
|
||||||
$project->openDocument('use', file_get_contents(__DIR__ . '/../../../fixtures/use.php'));
|
$project->openDocument('use', file_get_contents(__DIR__ . '/../../../../fixtures/use.php'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDefinitionFileBeginning() {
|
public function testDefinitionFileBeginning() {
|
|
@ -0,0 +1,78 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace LanguageServer\Tests\Server\TextDocument\References;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use LanguageServer\Tests\MockProtocolStream;
|
||||||
|
use LanguageServer\{Server, LanguageClient, Project};
|
||||||
|
use LanguageServer\Protocol\{TextDocumentIdentifier, Position, ReferenceContext};
|
||||||
|
|
||||||
|
class GlobalFallbackTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Server\TextDocument
|
||||||
|
*/
|
||||||
|
private $textDocument;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$client = new LanguageClient(new MockProtocolStream());
|
||||||
|
$project = new Project($client);
|
||||||
|
$this->textDocument = new Server\TextDocument($project, $client);
|
||||||
|
$project->openDocument('global_fallback', file_get_contents(__DIR__ . '/../../../../fixtures/global_fallback.php'));
|
||||||
|
$project->openDocument('global_symbols', file_get_contents(__DIR__ . '/../../../../fixtures/global_symbols.php'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClassDoesNotFallback()
|
||||||
|
{
|
||||||
|
// class TestClass implements TestInterface
|
||||||
|
// Get references for TestClass
|
||||||
|
$result = $this->textDocument->references(new ReferenceContext, new TextDocumentIdentifier('global_symbols'), new Position(6, 9));
|
||||||
|
$this->assertEquals([], $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFallsBackForConstants()
|
||||||
|
{
|
||||||
|
// const TEST_CONST = 123;
|
||||||
|
// Get references for TEST_CONST
|
||||||
|
$result = $this->textDocument->references(new ReferenceContext, new TextDocumentIdentifier('global_symbols'), new Position(4, 13));
|
||||||
|
$this->assertEquals([
|
||||||
|
[
|
||||||
|
'uri' => 'global_fallback',
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 6,
|
||||||
|
'character' => 5
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 6,
|
||||||
|
'character' => 15
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFallsBackForFunctions()
|
||||||
|
{
|
||||||
|
// function test_function()
|
||||||
|
// Get references for test_function
|
||||||
|
$result = $this->textDocument->references(new ReferenceContext, new TextDocumentIdentifier('global_symbols'), new Position(33, 16));
|
||||||
|
$this->assertEquals([
|
||||||
|
[
|
||||||
|
'uri' => 'global_fallback',
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 5,
|
||||||
|
'character' => 0
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 5,
|
||||||
|
'character' => 13
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,349 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace LanguageServer\Tests\Server\TextDocument\References;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use LanguageServer\Tests\MockProtocolStream;
|
||||||
|
use LanguageServer\{Server, LanguageClient, Project};
|
||||||
|
use LanguageServer\Protocol\{TextDocumentIdentifier, Position, ReferenceContext};
|
||||||
|
use function LanguageServer\pathToUri;
|
||||||
|
|
||||||
|
class GlobalTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Server\TextDocument
|
||||||
|
*/
|
||||||
|
private $textDocument;
|
||||||
|
|
||||||
|
private $symbolsUri;
|
||||||
|
private $referencesUri;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$client = new LanguageClient(new MockProtocolStream());
|
||||||
|
$project = new Project($client);
|
||||||
|
$this->textDocument = new Server\TextDocument($project, $client);
|
||||||
|
$this->symbolsUri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/global_symbols.php'));
|
||||||
|
$this->referencesUri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/global_references.php'));
|
||||||
|
$project->loadDocument($this->referencesUri);
|
||||||
|
$project->loadDocument($this->symbolsUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReferencesForClassLike()
|
||||||
|
{
|
||||||
|
// class TestClass implements TestInterface
|
||||||
|
// Get references for TestClass
|
||||||
|
$result = $this->textDocument->references(new ReferenceContext, new TextDocumentIdentifier($this->symbolsUri), new Position(6, 9));
|
||||||
|
$this->assertEquals([
|
||||||
|
// $obj = new TestClass();
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 4,
|
||||||
|
'character' => 11
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 4,
|
||||||
|
'character' => 20
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
// TestClass::staticTestMethod();
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 7,
|
||||||
|
'character' => 0
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 7,
|
||||||
|
'character' => 9
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
// echo TestClass::$staticTestProperty;
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 8,
|
||||||
|
'character' => 5
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 8,
|
||||||
|
'character' => 14
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
// TestClass::TEST_CLASS_CONST;
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 9,
|
||||||
|
'character' => 5
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 9,
|
||||||
|
'character' => 14
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
// function whatever(TestClass $param)
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 15,
|
||||||
|
'character' => 18
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 15,
|
||||||
|
'character' => 27
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
// function whatever(TestClass $param): TestClass
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 15,
|
||||||
|
'character' => 37
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 15,
|
||||||
|
'character' => 46
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReferencesForClassConstants()
|
||||||
|
{
|
||||||
|
// const TEST_CLASS_CONST = 123;
|
||||||
|
// Get references for TEST_CLASS_CONST
|
||||||
|
$result = $this->textDocument->references(new ReferenceContext, new TextDocumentIdentifier($this->symbolsUri), new Position(8, 19));
|
||||||
|
$this->assertEquals([
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 9,
|
||||||
|
'character' => 5
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 9,
|
||||||
|
'character' => 32
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReferencesForConstants()
|
||||||
|
{
|
||||||
|
// const TEST_CONST = 123;
|
||||||
|
// Get references for TEST_CONST
|
||||||
|
$result = $this->textDocument->references(new ReferenceContext, new TextDocumentIdentifier($this->symbolsUri), new Position(4, 13));
|
||||||
|
$this->assertEquals([
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 23,
|
||||||
|
'character' => 5
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 23,
|
||||||
|
'character' => 15
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReferencesForStaticMethods()
|
||||||
|
{
|
||||||
|
// public static function staticTestMethod()
|
||||||
|
// Get references for staticTestMethod
|
||||||
|
$result = $this->textDocument->references(new ReferenceContext, new TextDocumentIdentifier($this->symbolsUri), new Position(12, 35));
|
||||||
|
$this->assertEquals([
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 7,
|
||||||
|
'character' => 0
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 7,
|
||||||
|
'character' => 29
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReferencesForStaticProperties()
|
||||||
|
{
|
||||||
|
// public static $staticTestProperty;
|
||||||
|
// Get references for $staticTestProperty
|
||||||
|
$result = $this->textDocument->references(new ReferenceContext, new TextDocumentIdentifier($this->symbolsUri), new Position(9, 27));
|
||||||
|
$this->assertEquals([
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 8,
|
||||||
|
'character' => 5
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 8,
|
||||||
|
'character' => 35
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReferencesForMethods()
|
||||||
|
{
|
||||||
|
// public function testMethod($testParameter)
|
||||||
|
// Get references for testMethod
|
||||||
|
$result = $this->textDocument->references(new ReferenceContext, new TextDocumentIdentifier($this->symbolsUri), new Position(17, 24));
|
||||||
|
$this->assertEquals([
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 5,
|
||||||
|
'character' => 0
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 5,
|
||||||
|
'character' => 18
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReferencesForProperties()
|
||||||
|
{
|
||||||
|
// public $testProperty;
|
||||||
|
// Get references for testProperty
|
||||||
|
$result = $this->textDocument->references(new ReferenceContext, new TextDocumentIdentifier($this->symbolsUri), new Position(10, 15));
|
||||||
|
$this->assertEquals([
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 6,
|
||||||
|
'character' => 5
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 6,
|
||||||
|
'character' => 23
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReferencesForVariables()
|
||||||
|
{
|
||||||
|
// $var = 123;
|
||||||
|
// Get definition for $var
|
||||||
|
$result = $this->textDocument->references(new ReferenceContext, new TextDocumentIdentifier($this->referencesUri), new Position(13, 7));
|
||||||
|
$this->assertEquals([
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 12,
|
||||||
|
'character' => 0
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 12,
|
||||||
|
'character' => 4
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 13,
|
||||||
|
'character' => 5
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 13,
|
||||||
|
'character' => 9
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 20,
|
||||||
|
'character' => 9
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 20,
|
||||||
|
'character' => 13
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReferencesForFunctionParams()
|
||||||
|
{
|
||||||
|
// function whatever(TestClass $param): TestClass
|
||||||
|
// Get references for $param
|
||||||
|
$result = $this->textDocument->references(new ReferenceContext, new TextDocumentIdentifier($this->referencesUri), new Position(15, 32));
|
||||||
|
$this->assertEquals([
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 16,
|
||||||
|
'character' => 9
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 16,
|
||||||
|
'character' => 15
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReferencesForFunctions()
|
||||||
|
{
|
||||||
|
// function test_function()
|
||||||
|
// Get references for test_function
|
||||||
|
$result = $this->textDocument->references(new ReferenceContext, new TextDocumentIdentifier($this->symbolsUri), new Position(33, 16));
|
||||||
|
$this->assertEquals([
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 10,
|
||||||
|
'character' => 0
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 10,
|
||||||
|
'character' => 13
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,366 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace LanguageServer\Tests\Server\TextDocument\References;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use LanguageServer\Tests\MockProtocolStream;
|
||||||
|
use LanguageServer\{Server, LanguageClient, Project};
|
||||||
|
use LanguageServer\Protocol\{TextDocumentIdentifier, Position, ReferenceContext};
|
||||||
|
use function LanguageServer\pathToUri;
|
||||||
|
|
||||||
|
class NamespacedTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Server\TextDocument
|
||||||
|
*/
|
||||||
|
private $textDocument;
|
||||||
|
|
||||||
|
private $symbolsUri;
|
||||||
|
private $referencesUri;
|
||||||
|
private $useUri;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$client = new LanguageClient(new MockProtocolStream());
|
||||||
|
$project = new Project($client);
|
||||||
|
$this->textDocument = new Server\TextDocument($project, $client);
|
||||||
|
$this->symbolsUri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/symbols.php'));
|
||||||
|
$this->referencesUri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/references.php'));
|
||||||
|
$this->useUri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/use.php'));
|
||||||
|
$project->loadDocument($this->referencesUri);
|
||||||
|
$project->loadDocument($this->symbolsUri);
|
||||||
|
$project->loadDocument($this->useUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReferencesForClassLike()
|
||||||
|
{
|
||||||
|
// class TestClass implements TestInterface
|
||||||
|
// Get references for TestClass
|
||||||
|
$result = $this->textDocument->references(new ReferenceContext, new TextDocumentIdentifier($this->symbolsUri), new Position(6, 9));
|
||||||
|
$this->assertEquals([
|
||||||
|
// $obj = new TestClass();
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 4,
|
||||||
|
'character' => 11
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 4,
|
||||||
|
'character' => 20
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
// TestClass::staticTestMethod();
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 7,
|
||||||
|
'character' => 0
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 7,
|
||||||
|
'character' => 9
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
// echo TestClass::$staticTestProperty;
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 8,
|
||||||
|
'character' => 5
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 8,
|
||||||
|
'character' => 14
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
// TestClass::TEST_CLASS_CONST;
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 9,
|
||||||
|
'character' => 5
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 9,
|
||||||
|
'character' => 14
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
// function whatever(TestClass $param)
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 15,
|
||||||
|
'character' => 18
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 15,
|
||||||
|
'character' => 27
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
// function whatever(TestClass $param): TestClass
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 15,
|
||||||
|
'character' => 37
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 15,
|
||||||
|
'character' => 46
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
// use TestNamespace\TestClass;
|
||||||
|
[
|
||||||
|
'uri' => $this->useUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 4,
|
||||||
|
'character' => 4
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 4,
|
||||||
|
'character' => 27
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReferencesForClassConstants()
|
||||||
|
{
|
||||||
|
// const TEST_CLASS_CONST = 123;
|
||||||
|
// Get references for TEST_CLASS_CONST
|
||||||
|
$result = $this->textDocument->references(new ReferenceContext, new TextDocumentIdentifier($this->symbolsUri), new Position(8, 19));
|
||||||
|
$this->assertEquals([
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 9,
|
||||||
|
'character' => 5
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 9,
|
||||||
|
'character' => 32
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReferencesForConstants()
|
||||||
|
{
|
||||||
|
// const TEST_CONST = 123;
|
||||||
|
// Get references for TEST_CONST
|
||||||
|
$result = $this->textDocument->references(new ReferenceContext, new TextDocumentIdentifier($this->symbolsUri), new Position(4, 13));
|
||||||
|
$this->assertEquals([
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 23,
|
||||||
|
'character' => 5
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 23,
|
||||||
|
'character' => 15
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReferencesForStaticMethods()
|
||||||
|
{
|
||||||
|
// public static function staticTestMethod()
|
||||||
|
// Get references for staticTestMethod
|
||||||
|
$result = $this->textDocument->references(new ReferenceContext, new TextDocumentIdentifier($this->symbolsUri), new Position(12, 35));
|
||||||
|
$this->assertEquals([
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 7,
|
||||||
|
'character' => 0
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 7,
|
||||||
|
'character' => 29
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReferencesForStaticProperties()
|
||||||
|
{
|
||||||
|
// public static $staticTestProperty;
|
||||||
|
// Get references for $staticTestProperty
|
||||||
|
$result = $this->textDocument->references(new ReferenceContext, new TextDocumentIdentifier($this->symbolsUri), new Position(9, 27));
|
||||||
|
$this->assertEquals([
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 8,
|
||||||
|
'character' => 5
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 8,
|
||||||
|
'character' => 35
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReferencesForMethods()
|
||||||
|
{
|
||||||
|
// public function testMethod($testParameter)
|
||||||
|
// Get references for testMethod
|
||||||
|
$result = $this->textDocument->references(new ReferenceContext, new TextDocumentIdentifier($this->symbolsUri), new Position(17, 24));
|
||||||
|
$this->assertEquals([
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 5,
|
||||||
|
'character' => 0
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 5,
|
||||||
|
'character' => 18
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReferencesForProperties()
|
||||||
|
{
|
||||||
|
// public $testProperty;
|
||||||
|
// Get references for testProperty
|
||||||
|
$result = $this->textDocument->references(new ReferenceContext, new TextDocumentIdentifier($this->symbolsUri), new Position(10, 15));
|
||||||
|
$this->assertEquals([
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 6,
|
||||||
|
'character' => 5
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 6,
|
||||||
|
'character' => 23
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReferencesForVariables()
|
||||||
|
{
|
||||||
|
// $var = 123;
|
||||||
|
// Get definition for $var
|
||||||
|
$result = $this->textDocument->references(new ReferenceContext, new TextDocumentIdentifier($this->referencesUri), new Position(13, 7));
|
||||||
|
$this->assertEquals([
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 12,
|
||||||
|
'character' => 0
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 12,
|
||||||
|
'character' => 4
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 13,
|
||||||
|
'character' => 5
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 13,
|
||||||
|
'character' => 9
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 20,
|
||||||
|
'character' => 9
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 20,
|
||||||
|
'character' => 13
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReferencesForFunctionParams()
|
||||||
|
{
|
||||||
|
// function whatever(TestClass $param): TestClass
|
||||||
|
// Get references for $param
|
||||||
|
$result = $this->textDocument->references(new ReferenceContext, new TextDocumentIdentifier($this->referencesUri), new Position(15, 32));
|
||||||
|
$this->assertEquals([
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 16,
|
||||||
|
'character' => 9
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 16,
|
||||||
|
'character' => 15
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReferencesForFunctions()
|
||||||
|
{
|
||||||
|
// function test_function()
|
||||||
|
// Get references for test_function
|
||||||
|
$result = $this->textDocument->references(new ReferenceContext, new TextDocumentIdentifier($this->symbolsUri), new Position(33, 16));
|
||||||
|
$this->assertEquals([
|
||||||
|
[
|
||||||
|
'uri' => $this->referencesUri,
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 10,
|
||||||
|
'character' => 0
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 10,
|
||||||
|
'character' => 13
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], json_decode(json_encode($result), true));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue