Merge branch 'master' into feature/autocomplete-speedup
commit
3bda390c3d
|
@ -23,7 +23,7 @@ install:
|
||||||
- composer install --prefer-dist --no-interaction
|
- composer install --prefer-dist --no-interaction
|
||||||
script:
|
script:
|
||||||
- vendor/bin/phpcs -n
|
- vendor/bin/phpcs -n
|
||||||
- vendor/bin/phpunit --coverage-clover=coverage.xml
|
- vendor/bin/phpunit --coverage-clover=coverage.xml --colors=always
|
||||||
- bash <(curl -s https://codecov.io/bash)
|
- bash <(curl -s https://codecov.io/bash)
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
|
@ -1,29 +1,27 @@
|
||||||
{
|
{
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"name": "Listen for XDebug",
|
"name": "PHPUnit",
|
||||||
"type": "php",
|
"type": "php",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"port": 9000
|
"program": "${workspaceRoot}/vendor/phpunit/phpunit/phpunit",
|
||||||
},
|
// "args": ["--filter", "testDefinitionForSelfKeyword"],
|
||||||
{
|
"cwd": "${workspaceRoot}"
|
||||||
"name": "Launch currently open script",
|
},
|
||||||
"type": "php",
|
{
|
||||||
"request": "launch",
|
"name": "Listen for XDebug",
|
||||||
"program": "${file}",
|
"type": "php",
|
||||||
"cwd": "${fileDirname}",
|
"request": "launch",
|
||||||
"port": 9000
|
"port": 9000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "PHPUnit",
|
"name": "Launch currently open script",
|
||||||
"type": "php",
|
"type": "php",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${workspaceRoot}/vendor/phpunit/phpunit/phpunit",
|
"program": "${file}",
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${fileDirname}",
|
||||||
"args": [
|
"port": 9000
|
||||||
// "--filter", "CompletionTest"
|
}
|
||||||
]
|
]
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<
|
|
@ -10,7 +10,9 @@ use LanguageServer\Protocol\{
|
||||||
Position,
|
Position,
|
||||||
CompletionList,
|
CompletionList,
|
||||||
CompletionItem,
|
CompletionItem,
|
||||||
CompletionItemKind
|
CompletionItemKind,
|
||||||
|
CompletionContext,
|
||||||
|
CompletionTriggerKind
|
||||||
};
|
};
|
||||||
use Microsoft\PhpParser;
|
use Microsoft\PhpParser;
|
||||||
use Microsoft\PhpParser\Node;
|
use Microsoft\PhpParser\Node;
|
||||||
|
@ -122,9 +124,10 @@ class CompletionProvider
|
||||||
*
|
*
|
||||||
* @param PhpDocument $doc The opened document
|
* @param PhpDocument $doc The opened document
|
||||||
* @param Position $pos The cursor position
|
* @param Position $pos The cursor position
|
||||||
|
* @param CompletionContext $context The completion context
|
||||||
* @return CompletionList
|
* @return CompletionList
|
||||||
*/
|
*/
|
||||||
public function provideCompletion(PhpDocument $doc, Position $pos): CompletionList
|
public function provideCompletion(PhpDocument $doc, Position $pos, CompletionContext $context = null): CompletionList
|
||||||
{
|
{
|
||||||
// This can be made much more performant if the tree follows specific invariants.
|
// This can be made much more performant if the tree follows specific invariants.
|
||||||
$node = $doc->getNodeAtPosition($pos);
|
$node = $doc->getNodeAtPosition($pos);
|
||||||
|
@ -152,7 +155,21 @@ class CompletionProvider
|
||||||
|
|
||||||
// Inspect the type of expression under the cursor
|
// Inspect the type of expression under the cursor
|
||||||
|
|
||||||
if ($node === null || $node instanceof Node\Statement\InlineHtml || $pos == new Position(0, 0)) {
|
$content = $doc->getContent();
|
||||||
|
$offset = $pos->toOffset($content);
|
||||||
|
if (
|
||||||
|
$node === null
|
||||||
|
|| (
|
||||||
|
$node instanceof Node\Statement\InlineHtml
|
||||||
|
&& (
|
||||||
|
$context === null
|
||||||
|
// Make sure to not suggest on the > trigger character in HTML
|
||||||
|
|| $context->triggerKind === CompletionTriggerKind::INVOKED
|
||||||
|
|| $context->triggerCharacter === '<'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|| $pos == new Position(0, 0)
|
||||||
|
) {
|
||||||
// HTML, beginning of file
|
// HTML, beginning of file
|
||||||
|
|
||||||
// Inside HTML and at the beginning of the file, propose <?php
|
// Inside HTML and at the beginning of the file, propose <?php
|
||||||
|
|
|
@ -264,13 +264,38 @@ class DefinitionResolver
|
||||||
// Other references are references to a global symbol that have an FQN
|
// Other references are references to a global symbol that have an FQN
|
||||||
// Find out the FQN
|
// Find out the FQN
|
||||||
$fqn = $this->resolveReferenceNodeToFqn($node);
|
$fqn = $this->resolveReferenceNodeToFqn($node);
|
||||||
if ($fqn === null) {
|
if (!$fqn) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($fqn === 'self' || $fqn === 'static') {
|
||||||
|
// Resolve self and static keywords to the containing class
|
||||||
|
// (This is not 100% correct for static but better than nothing)
|
||||||
|
$classNode = $node->getFirstAncestor(Node\Statement\ClassDeclaration::class);
|
||||||
|
if (!$classNode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$fqn = (string)$classNode->getNamespacedName();
|
||||||
|
if (!$fqn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if ($fqn === 'parent') {
|
||||||
|
// Resolve parent keyword to the base class FQN
|
||||||
|
$classNode = $node->getFirstAncestor(Node\Statement\ClassDeclaration::class);
|
||||||
|
if (!$classNode || !$classNode->classBaseClause || !$classNode->classBaseClause->baseClass) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$fqn = (string)$classNode->classBaseClause->baseClass->getResolvedName();
|
||||||
|
if (!$fqn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If the node is a function or constant, it could be namespaced, but PHP falls back to global
|
// 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
|
// http://php.net/manual/en/language.namespaces.fallback.php
|
||||||
// TODO - verify that this is not a method
|
// TODO - verify that this is not a method
|
||||||
$globalFallback = ParserHelpers\isConstantFetch($node) || $parent instanceof Node\Expression\CallExpression;
|
$globalFallback = ParserHelpers\isConstantFetch($node) || $parent instanceof Node\Expression\CallExpression;
|
||||||
|
|
||||||
// Return the Definition object from the index index
|
// Return the Definition object from the index index
|
||||||
return $this->index->getDefinition($fqn, $globalFallback);
|
return $this->index->getDefinition($fqn, $globalFallback);
|
||||||
}
|
}
|
||||||
|
@ -278,6 +303,7 @@ class DefinitionResolver
|
||||||
/**
|
/**
|
||||||
* Given any node, returns the FQN of the symbol that is referenced
|
* Given any node, returns the FQN of the symbol that is referenced
|
||||||
* Returns null if the FQN could not be resolved or the reference node references a variable
|
* Returns null if the FQN could not be resolved or the reference node references a variable
|
||||||
|
* May also return "static", "self" or "parent"
|
||||||
*
|
*
|
||||||
* @param Node $node
|
* @param Node $node
|
||||||
* @return string|null
|
* @return string|null
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace LanguageServer\Protocol;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains additional information about the context in which a completion request is triggered.
|
||||||
|
*/
|
||||||
|
class CompletionContext
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* How the completion was triggered.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $triggerKind;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The trigger character (a single character) that has trigger code complete.
|
||||||
|
* Is null if `triggerKind !== CompletionTriggerKind::TRIGGER_CHARACTER`
|
||||||
|
*
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
public $triggerCharacter;
|
||||||
|
|
||||||
|
public function __construct(int $triggerKind, string $triggerCharacter = null)
|
||||||
|
{
|
||||||
|
$this->triggerKind = $triggerKind;
|
||||||
|
$this->triggerCharacter = $triggerCharacter;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace LanguageServer\Protocol;
|
||||||
|
|
||||||
|
class CompletionTriggerKind
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Completion was triggered by invoking it manuall or using API.
|
||||||
|
*/
|
||||||
|
const INVOKED = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Completion was triggered by a trigger character.
|
||||||
|
*/
|
||||||
|
const TRIGGER_CHARACTER = 2;
|
||||||
|
}
|
|
@ -20,7 +20,8 @@ use LanguageServer\Protocol\{
|
||||||
SymbolLocationInformation,
|
SymbolLocationInformation,
|
||||||
TextDocumentIdentifier,
|
TextDocumentIdentifier,
|
||||||
TextDocumentItem,
|
TextDocumentItem,
|
||||||
VersionedTextDocumentIdentifier
|
VersionedTextDocumentIdentifier,
|
||||||
|
CompletionContext
|
||||||
};
|
};
|
||||||
use Microsoft\PhpParser;
|
use Microsoft\PhpParser;
|
||||||
use Microsoft\PhpParser\Node;
|
use Microsoft\PhpParser\Node;
|
||||||
|
@ -334,13 +335,14 @@ class TextDocument
|
||||||
*
|
*
|
||||||
* @param TextDocumentIdentifier The text document
|
* @param TextDocumentIdentifier The text document
|
||||||
* @param Position $position The position
|
* @param Position $position The position
|
||||||
|
* @param CompletionContext|null $context The completion context
|
||||||
* @return Promise <CompletionItem[]|CompletionList>
|
* @return Promise <CompletionItem[]|CompletionList>
|
||||||
*/
|
*/
|
||||||
public function completion(TextDocumentIdentifier $textDocument, Position $position): Promise
|
public function completion(TextDocumentIdentifier $textDocument, Position $position, CompletionContext $context = null): Promise
|
||||||
{
|
{
|
||||||
return coroutine(function () use ($textDocument, $position) {
|
return coroutine(function () use ($textDocument, $position, $context) {
|
||||||
$document = yield $this->documentLoader->getOrLoad($textDocument->uri);
|
$document = yield $this->documentLoader->getOrLoad($textDocument->uri);
|
||||||
return $this->completionProvider->provideCompletion($document, $position);
|
return $this->completionProvider->provideCompletion($document, $position, $context);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -140,8 +140,9 @@ class TreeAnalyzer
|
||||||
$this->definitionNodes[$fqn] = $node;
|
$this->definitionNodes[$fqn] = $node;
|
||||||
$this->definitions[$fqn] = $this->definitionResolver->createDefinitionFromNode($node, $fqn);
|
$this->definitions[$fqn] = $this->definitionResolver->createDefinitionFromNode($node, $fqn);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
$parent = $node->parent;
|
$parent = $node->parent;
|
||||||
if (!(
|
if (
|
||||||
(
|
(
|
||||||
// $node->parent instanceof Node\Expression\ScopedPropertyAccessExpression ||
|
// $node->parent instanceof Node\Expression\ScopedPropertyAccessExpression ||
|
||||||
($node instanceof Node\Expression\ScopedPropertyAccessExpression ||
|
($node instanceof Node\Expression\ScopedPropertyAccessExpression ||
|
||||||
|
@ -150,41 +151,68 @@ class TreeAnalyzer
|
||||||
$node->parent instanceof Node\Expression\CallExpression ||
|
$node->parent instanceof Node\Expression\CallExpression ||
|
||||||
$node->memberName instanceof PhpParser\Token
|
$node->memberName instanceof PhpParser\Token
|
||||||
))
|
))
|
||||||
|| ($parent instanceof Node\Statement\NamespaceDefinition && $parent->name !== null && $parent->name->getStart() === $node->getStart()))
|
|| ($parent instanceof Node\Statement\NamespaceDefinition && $parent->name !== null && $parent->name->getStart() === $node->getStart())
|
||||||
) {
|
) {
|
||||||
$fqn = $this->definitionResolver->resolveReferenceNodeToFqn($node);
|
return;
|
||||||
if ($fqn !== null) {
|
}
|
||||||
$this->addReference($fqn, $node);
|
|
||||||
|
|
||||||
if (
|
$fqn = $this->definitionResolver->resolveReferenceNodeToFqn($node);
|
||||||
$node instanceof Node\QualifiedName
|
if (!$fqn) {
|
||||||
&& ($node->isQualifiedName() || $node->parent instanceof Node\NamespaceUseClause)
|
return;
|
||||||
&& !($parent instanceof Node\Statement\NamespaceDefinition && $parent->name->getStart() === $node->getStart()
|
}
|
||||||
)
|
|
||||||
) {
|
|
||||||
// Add references for each referenced namespace
|
|
||||||
$ns = $fqn;
|
|
||||||
while (($pos = strrpos($ns, '\\')) !== false) {
|
|
||||||
$ns = substr($ns, 0, $pos);
|
|
||||||
$this->addReference($ns, $node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Namespaced constant access and function calls also need to register a reference
|
if ($fqn === 'self' || $fqn === 'static') {
|
||||||
// to the global version because PHP falls back to global at runtime
|
// Resolve self and static keywords to the containing class
|
||||||
// http://php.net/manual/en/language.namespaces.fallback.php
|
// (This is not 100% correct for static but better than nothing)
|
||||||
if (ParserHelpers\isConstantFetch($node) ||
|
$classNode = $node->getFirstAncestor(Node\Statement\ClassDeclaration::class);
|
||||||
($parent instanceof Node\Expression\CallExpression
|
if (!$classNode) {
|
||||||
&& !(
|
return;
|
||||||
$node instanceof Node\Expression\ScopedPropertyAccessExpression ||
|
}
|
||||||
$node instanceof Node\Expression\MemberAccessExpression
|
$fqn = (string)$classNode->getNamespacedName();
|
||||||
))) {
|
if (!$fqn) {
|
||||||
$parts = explode('\\', $fqn);
|
return;
|
||||||
if (count($parts) > 1) {
|
}
|
||||||
$globalFqn = end($parts);
|
} else if ($fqn === 'parent') {
|
||||||
$this->addReference($globalFqn, $node);
|
// Resolve parent keyword to the base class FQN
|
||||||
}
|
$classNode = $node->getFirstAncestor(Node\Statement\ClassDeclaration::class);
|
||||||
}
|
if (!$classNode || !$classNode->classBaseClause || !$classNode->classBaseClause->baseClass) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$fqn = (string)$classNode->classBaseClause->baseClass->getResolvedName();
|
||||||
|
if (!$fqn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->addReference($fqn, $node);
|
||||||
|
|
||||||
|
if (
|
||||||
|
$node instanceof Node\QualifiedName
|
||||||
|
&& ($node->isQualifiedName() || $node->parent instanceof Node\NamespaceUseClause)
|
||||||
|
&& !($parent instanceof Node\Statement\NamespaceDefinition && $parent->name->getStart() === $node->getStart()
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
// Add references for each referenced namespace
|
||||||
|
$ns = $fqn;
|
||||||
|
while (($pos = strrpos($ns, '\\')) !== false) {
|
||||||
|
$ns = substr($ns, 0, $pos);
|
||||||
|
$this->addReference($ns, $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
|
||||||
|
if (ParserHelpers\isConstantFetch($node) ||
|
||||||
|
($parent instanceof Node\Expression\CallExpression
|
||||||
|
&& !(
|
||||||
|
$node instanceof Node\Expression\ScopedPropertyAccessExpression ||
|
||||||
|
$node instanceof Node\Expression\MemberAccessExpression
|
||||||
|
))) {
|
||||||
|
$parts = explode('\\', $fqn);
|
||||||
|
if (count($parts) > 1) {
|
||||||
|
$globalFqn = end($parts);
|
||||||
|
$this->addReference($globalFqn, $node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,15 +122,16 @@ abstract class ServerTestCase extends TestCase
|
||||||
0 => new Location($referencesUri, new Range(new Position(29, 5), new Position(29, 15)))
|
0 => new Location($referencesUri, new Range(new Position(29, 5), new Position(29, 15)))
|
||||||
],
|
],
|
||||||
'TestNamespace\\TestClass' => [
|
'TestNamespace\\TestClass' => [
|
||||||
0 => new Location($symbolsUri , new Range(new Position(99, 25), new Position(99, 34))), // class ChildClass extends TestClass {}
|
0 => new Location($symbolsUri, new Range(new Position(48, 13), new Position(48, 17))), // echo self::TEST_CLASS_CONST;
|
||||||
1 => new Location($referencesUri, new Range(new Position( 4, 11), new Position( 4, 20))), // $obj = new TestClass();
|
1 => new Location($symbolsUri , new Range(new Position(99, 25), new Position(99, 34))), // class ChildClass extends TestClass {}
|
||||||
2 => new Location($referencesUri, new Range(new Position( 7, 0), new Position( 7, 9))), // TestClass::staticTestMethod();
|
2 => new Location($referencesUri, new Range(new Position( 4, 11), new Position( 4, 20))), // $obj = new TestClass();
|
||||||
3 => new Location($referencesUri, new Range(new Position( 8, 5), new Position( 8, 14))), // echo TestClass::$staticTestProperty;
|
3 => new Location($referencesUri, new Range(new Position( 7, 0), new Position( 7, 9))), // TestClass::staticTestMethod();
|
||||||
4 => new Location($referencesUri, new Range(new Position( 9, 5), new Position( 9, 14))), // TestClass::TEST_CLASS_CONST;
|
4 => new Location($referencesUri, new Range(new Position( 8, 5), new Position( 8, 14))), // echo TestClass::$staticTestProperty;
|
||||||
5 => new Location($referencesUri, new Range(new Position(21, 18), new Position(21, 27))), // function whatever(TestClass $param)
|
5 => new Location($referencesUri, new Range(new Position( 9, 5), new Position( 9, 14))), // TestClass::TEST_CLASS_CONST;
|
||||||
6 => new Location($referencesUri, new Range(new Position(21, 37), new Position(21, 46))), // function whatever(TestClass $param): TestClass
|
6 => new Location($referencesUri, new Range(new Position(21, 18), new Position(21, 27))), // function whatever(TestClass $param)
|
||||||
7 => new Location($referencesUri, new Range(new Position(39, 0), new Position(39, 9))), // TestClass::$staticTestProperty[123]->testProperty;
|
7 => new Location($referencesUri, new Range(new Position(21, 37), new Position(21, 46))), // function whatever(TestClass $param): TestClass
|
||||||
8 => new Location($useUri, new Range(new Position( 4, 4), new Position( 4, 27))), // use TestNamespace\TestClass;
|
8 => new Location($referencesUri, new Range(new Position(39, 0), new Position(39, 9))), // TestClass::$staticTestProperty[123]->testProperty;
|
||||||
|
9 => new Location($useUri, new Range(new Position( 4, 4), new Position( 4, 27))), // use TestNamespace\TestClass;
|
||||||
],
|
],
|
||||||
'TestNamespace\\TestChild' => [
|
'TestNamespace\\TestChild' => [
|
||||||
0 => new Location($referencesUri, new Range(new Position(42, 5), new Position(42, 25))), // echo $child->testProperty;
|
0 => new Location($referencesUri, new Range(new Position(42, 5), new Position(42, 25))), // echo $child->testProperty;
|
||||||
|
@ -176,14 +177,15 @@ abstract class ServerTestCase extends TestCase
|
||||||
1 => new Location($globalReferencesUri, new Range(new Position(29, 5), new Position(29, 15)))
|
1 => new Location($globalReferencesUri, new Range(new Position(29, 5), new Position(29, 15)))
|
||||||
],
|
],
|
||||||
'TestClass' => [
|
'TestClass' => [
|
||||||
0 => new Location($globalSymbolsUri, new Range(new Position(99, 25), new Position(99, 34))), // class ChildClass extends TestClass {}
|
0 => new Location($globalSymbolsUri, new Range(new Position(48, 13), new Position(48, 17))), // echo self::TEST_CLASS_CONST;
|
||||||
1 => new Location($globalReferencesUri, new Range(new Position( 4, 11), new Position( 4, 20))), // $obj = new TestClass();
|
1 => new Location($globalSymbolsUri, new Range(new Position(99, 25), new Position(99, 34))), // class ChildClass extends TestClass {}
|
||||||
2 => new Location($globalReferencesUri, new Range(new Position( 7, 0), new Position( 7, 9))), // TestClass::staticTestMethod();
|
2 => new Location($globalReferencesUri, new Range(new Position( 4, 11), new Position( 4, 20))), // $obj = new TestClass();
|
||||||
3 => new Location($globalReferencesUri, new Range(new Position( 8, 5), new Position( 8, 14))), // echo TestClass::$staticTestProperty;
|
3 => new Location($globalReferencesUri, new Range(new Position( 7, 0), new Position( 7, 9))), // TestClass::staticTestMethod();
|
||||||
4 => new Location($globalReferencesUri, new Range(new Position( 9, 5), new Position( 9, 14))), // TestClass::TEST_CLASS_CONST;
|
4 => new Location($globalReferencesUri, new Range(new Position( 8, 5), new Position( 8, 14))), // echo TestClass::$staticTestProperty;
|
||||||
5 => new Location($globalReferencesUri, new Range(new Position(21, 18), new Position(21, 27))), // function whatever(TestClass $param)
|
5 => new Location($globalReferencesUri, new Range(new Position( 9, 5), new Position( 9, 14))), // TestClass::TEST_CLASS_CONST;
|
||||||
6 => new Location($globalReferencesUri, new Range(new Position(21, 37), new Position(21, 46))), // function whatever(TestClass $param): TestClass
|
6 => new Location($globalReferencesUri, new Range(new Position(21, 18), new Position(21, 27))), // function whatever(TestClass $param)
|
||||||
7 => new Location($globalReferencesUri, new Range(new Position(39, 0), new Position(39, 9))), // TestClass::$staticTestProperty[123]->testProperty;
|
7 => new Location($globalReferencesUri, new Range(new Position(21, 37), new Position(21, 46))), // function whatever(TestClass $param): TestClass
|
||||||
|
8 => new Location($globalReferencesUri, new Range(new Position(39, 0), new Position(39, 9))), // TestClass::$staticTestProperty[123]->testProperty;
|
||||||
],
|
],
|
||||||
'TestChild' => [
|
'TestChild' => [
|
||||||
0 => new Location($globalReferencesUri, new Range(new Position(42, 5), new Position(42, 25))), // echo $child->testProperty;
|
0 => new Location($globalReferencesUri, new Range(new Position(42, 5), new Position(42, 25))), // echo $child->testProperty;
|
||||||
|
|
|
@ -17,7 +17,9 @@ use LanguageServer\Protocol\{
|
||||||
Position,
|
Position,
|
||||||
CompletionList,
|
CompletionList,
|
||||||
CompletionItem,
|
CompletionItem,
|
||||||
CompletionItemKind
|
CompletionItemKind,
|
||||||
|
CompletionContext,
|
||||||
|
CompletionTriggerKind
|
||||||
};
|
};
|
||||||
use function LanguageServer\pathToUri;
|
use function LanguageServer\pathToUri;
|
||||||
|
|
||||||
|
@ -464,6 +466,41 @@ class CompletionTest extends TestCase
|
||||||
], true), $items);
|
], true), $items);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testHtmlPrefixShouldNotTriggerCompletion()
|
||||||
|
{
|
||||||
|
$completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/html_no_completion.php');
|
||||||
|
$this->loader->open($completionUri, file_get_contents($completionUri));
|
||||||
|
$items = $this->textDocument->completion(
|
||||||
|
new TextDocumentIdentifier($completionUri),
|
||||||
|
new Position(0, 1),
|
||||||
|
new CompletionContext(CompletionTriggerKind::TRIGGER_CHARACTER, '>')
|
||||||
|
)->wait();
|
||||||
|
$this->assertEquals(new CompletionList([], true), $items);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHtmlPrefixShouldTriggerCompletionIfManuallyInvoked()
|
||||||
|
{
|
||||||
|
$completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/html_no_completion.php');
|
||||||
|
$this->loader->open($completionUri, file_get_contents($completionUri));
|
||||||
|
$items = $this->textDocument->completion(
|
||||||
|
new TextDocumentIdentifier($completionUri),
|
||||||
|
new Position(0, 1),
|
||||||
|
new CompletionContext(CompletionTriggerKind::INVOKED)
|
||||||
|
)->wait();
|
||||||
|
$this->assertEquals(new CompletionList([
|
||||||
|
new CompletionItem(
|
||||||
|
'<?php',
|
||||||
|
CompletionItemKind::KEYWORD,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
new TextEdit(new Range(new Position(0, 1), new Position(0, 1)), '?php')
|
||||||
|
)
|
||||||
|
], true), $items);
|
||||||
|
}
|
||||||
|
|
||||||
public function testNamespace()
|
public function testNamespace()
|
||||||
{
|
{
|
||||||
$completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/namespace.php');
|
$completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/namespace.php');
|
||||||
|
|
|
@ -29,11 +29,23 @@ class GlobalTest extends ServerTestCase
|
||||||
$this->assertEquals([], $result);
|
$this->assertEquals([], $result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testDefinitionForSelfKeyword()
|
||||||
|
{
|
||||||
|
// echo self::TEST_CLASS_CONST;
|
||||||
|
// Get definition for self
|
||||||
|
$reference = $this->getReferenceLocations('TestClass')[0];
|
||||||
|
$result = $this->textDocument->definition(
|
||||||
|
new TextDocumentIdentifier($reference->uri),
|
||||||
|
$reference->range->start
|
||||||
|
)->wait();
|
||||||
|
$this->assertEquals($this->getDefinitionLocation('TestClass'), $result);
|
||||||
|
}
|
||||||
|
|
||||||
public function testDefinitionForClassLike()
|
public function testDefinitionForClassLike()
|
||||||
{
|
{
|
||||||
// $obj = new TestClass();
|
// $obj = new TestClass();
|
||||||
// Get definition for TestClass
|
// Get definition for TestClass
|
||||||
$reference = $this->getReferenceLocations('TestClass')[0];
|
$reference = $this->getReferenceLocations('TestClass')[1];
|
||||||
$result = $this->textDocument->definition(
|
$result = $this->textDocument->definition(
|
||||||
new TextDocumentIdentifier($reference->uri),
|
new TextDocumentIdentifier($reference->uri),
|
||||||
$reference->range->start
|
$reference->range->start
|
||||||
|
@ -45,7 +57,7 @@ class GlobalTest extends ServerTestCase
|
||||||
{
|
{
|
||||||
// TestClass::staticTestMethod();
|
// TestClass::staticTestMethod();
|
||||||
// Get definition for TestClass
|
// Get definition for TestClass
|
||||||
$reference = $this->getReferenceLocations('TestClass')[1];
|
$reference = $this->getReferenceLocations('TestClass')[2];
|
||||||
$result = $this->textDocument->definition(
|
$result = $this->textDocument->definition(
|
||||||
new TextDocumentIdentifier($reference->uri),
|
new TextDocumentIdentifier($reference->uri),
|
||||||
$reference->range->start
|
$reference->range->start
|
||||||
|
@ -57,7 +69,7 @@ class GlobalTest extends ServerTestCase
|
||||||
{
|
{
|
||||||
// echo TestClass::$staticTestProperty;
|
// echo TestClass::$staticTestProperty;
|
||||||
// Get definition for TestClass
|
// Get definition for TestClass
|
||||||
$reference = $this->getReferenceLocations('TestClass')[2];
|
$reference = $this->getReferenceLocations('TestClass')[3];
|
||||||
$result = $this->textDocument->definition(
|
$result = $this->textDocument->definition(
|
||||||
new TextDocumentIdentifier($reference->uri),
|
new TextDocumentIdentifier($reference->uri),
|
||||||
$reference->range->start
|
$reference->range->start
|
||||||
|
@ -69,7 +81,7 @@ class GlobalTest extends ServerTestCase
|
||||||
{
|
{
|
||||||
// TestClass::TEST_CLASS_CONST;
|
// TestClass::TEST_CLASS_CONST;
|
||||||
// Get definition for TestClass
|
// Get definition for TestClass
|
||||||
$reference = $this->getReferenceLocations('TestClass')[3];
|
$reference = $this->getReferenceLocations('TestClass')[4];
|
||||||
$result = $this->textDocument->definition(
|
$result = $this->textDocument->definition(
|
||||||
new TextDocumentIdentifier($reference->uri),
|
new TextDocumentIdentifier($reference->uri),
|
||||||
$reference->range->start
|
$reference->range->start
|
||||||
|
@ -213,7 +225,7 @@ class GlobalTest extends ServerTestCase
|
||||||
{
|
{
|
||||||
// function whatever(TestClass $param) {
|
// function whatever(TestClass $param) {
|
||||||
// Get definition for TestClass
|
// Get definition for TestClass
|
||||||
$reference = $this->getReferenceLocations('TestClass')[4];
|
$reference = $this->getReferenceLocations('TestClass')[5];
|
||||||
$result = $this->textDocument->definition(
|
$result = $this->textDocument->definition(
|
||||||
new TextDocumentIdentifier($reference->uri),
|
new TextDocumentIdentifier($reference->uri),
|
||||||
$reference->range->start
|
$reference->range->start
|
||||||
|
@ -225,7 +237,7 @@ class GlobalTest extends ServerTestCase
|
||||||
{
|
{
|
||||||
// function whatever(TestClass $param): TestClass {
|
// function whatever(TestClass $param): TestClass {
|
||||||
// Get definition for TestClass
|
// Get definition for TestClass
|
||||||
$reference = $this->getReferenceLocations('TestClass')[5];
|
$reference = $this->getReferenceLocations('TestClass')[6];
|
||||||
$result = $this->textDocument->definition(
|
$result = $this->textDocument->definition(
|
||||||
new TextDocumentIdentifier($reference->uri),
|
new TextDocumentIdentifier($reference->uri),
|
||||||
$reference->range->start
|
$reference->range->start
|
||||||
|
|
|
@ -34,7 +34,7 @@ class NamespacedTest extends GlobalTest
|
||||||
{
|
{
|
||||||
// use TestNamespace\TestClass;
|
// use TestNamespace\TestClass;
|
||||||
// Get definition for TestClass
|
// Get definition for TestClass
|
||||||
$reference = $this->getReferenceLocations('TestClass')[6];
|
$reference = $this->getReferenceLocations('TestClass')[7];
|
||||||
$result = $this->textDocument->definition(
|
$result = $this->textDocument->definition(
|
||||||
new TextDocumentIdentifier($reference->uri),
|
new TextDocumentIdentifier($reference->uri),
|
||||||
$reference->range->start
|
$reference->range->start
|
||||||
|
@ -46,7 +46,7 @@ class NamespacedTest extends GlobalTest
|
||||||
{
|
{
|
||||||
// use TestNamespace\{TestTrait, TestInterface};
|
// use TestNamespace\{TestTrait, TestInterface};
|
||||||
// Get definition for TestInterface
|
// Get definition for TestInterface
|
||||||
$reference = $this->getReferenceLocations('TestClass')[0];
|
$reference = $this->getReferenceLocations('TestClass')[1];
|
||||||
$result = $this->textDocument->definition(
|
$result = $this->textDocument->definition(
|
||||||
new TextDocumentIdentifier($reference->uri),
|
new TextDocumentIdentifier($reference->uri),
|
||||||
$reference->range->start
|
$reference->range->start
|
||||||
|
|
|
@ -15,7 +15,7 @@ class HoverTest extends ServerTestCase
|
||||||
{
|
{
|
||||||
// $obj = new TestClass();
|
// $obj = new TestClass();
|
||||||
// Get hover for TestClass
|
// Get hover for TestClass
|
||||||
$reference = $this->getReferenceLocations('TestClass')[0];
|
$reference = $this->getReferenceLocations('TestClass')[1];
|
||||||
$result = $this->textDocument->hover(
|
$result = $this->textDocument->hover(
|
||||||
new TextDocumentIdentifier($reference->uri),
|
new TextDocumentIdentifier($reference->uri),
|
||||||
$reference->range->start
|
$reference->range->start
|
||||||
|
|
|
@ -151,7 +151,7 @@ class GlobalTest extends ServerTestCase
|
||||||
{
|
{
|
||||||
// $obj = new TestClass();
|
// $obj = new TestClass();
|
||||||
// Get references for TestClass
|
// Get references for TestClass
|
||||||
$reference = $this->getReferenceLocations('TestClass')[0];
|
$reference = $this->getReferenceLocations('TestClass')[1];
|
||||||
$result = $this->textDocument->references(
|
$result = $this->textDocument->references(
|
||||||
new ReferenceContext,
|
new ReferenceContext,
|
||||||
new TextDocumentIdentifier($reference->uri),
|
new TextDocumentIdentifier($reference->uri),
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"Fixtures\\Prophecy\\EmptyClass": [
|
"Fixtures\\Prophecy\\EmptyClass": [
|
||||||
"./WithReturnTypehints.php"
|
"./WithReturnTypehints.php"
|
||||||
],
|
],
|
||||||
"self": [
|
"Fixtures\\Prophecy\\WithReturnTypehints": [
|
||||||
"./WithReturnTypehints.php"
|
"./WithReturnTypehints.php"
|
||||||
],
|
],
|
||||||
"Fixtures\\Prophecy\\__CLASS__": [
|
"Fixtures\\Prophecy\\__CLASS__": [
|
||||||
|
@ -11,9 +11,6 @@
|
||||||
],
|
],
|
||||||
"__CLASS__": [
|
"__CLASS__": [
|
||||||
"./WithReturnTypehints.php"
|
"./WithReturnTypehints.php"
|
||||||
],
|
|
||||||
"parent": [
|
|
||||||
"./WithReturnTypehints.php"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
{
|
{
|
||||||
"references": [],
|
"references": {
|
||||||
|
"A": [
|
||||||
|
"./nameToken.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
"A": {
|
"A": {
|
||||||
"fqn": "A",
|
"fqn": "A",
|
||||||
|
|
|
@ -2,9 +2,6 @@
|
||||||
"references": {
|
"references": {
|
||||||
"MyNamespace\\B": [
|
"MyNamespace\\B": [
|
||||||
"./parent1.php"
|
"./parent1.php"
|
||||||
],
|
|
||||||
"parent": [
|
|
||||||
"./parent1.php"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
|
|
|
@ -5,9 +5,6 @@
|
||||||
],
|
],
|
||||||
"MyNamespace\\B->b()": [
|
"MyNamespace\\B->b()": [
|
||||||
"./parent3.php"
|
"./parent3.php"
|
||||||
],
|
|
||||||
"parent": [
|
|
||||||
"./parent3.php"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"MyNamespace\\A::b()": [
|
"MyNamespace\\A::b()": [
|
||||||
"./self1.php"
|
"./self1.php"
|
||||||
],
|
],
|
||||||
"self": [
|
"MyNamespace\\A": [
|
||||||
"./self1.php"
|
"./self1.php"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"MyNamespace\\A::b()": [
|
"MyNamespace\\A::b()": [
|
||||||
"./self2.php"
|
"./self2.php"
|
||||||
],
|
],
|
||||||
"self": [
|
"MyNamespace\\A": [
|
||||||
"./self2.php"
|
"./self2.php"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"MyNamespace\\A->b()": [
|
"MyNamespace\\A->b()": [
|
||||||
"./self3.php"
|
"./self3.php"
|
||||||
],
|
],
|
||||||
"self": [
|
"MyNamespace\\A": [
|
||||||
"./self3.php"
|
"./self3.php"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"references": {
|
"references": {
|
||||||
"self": [
|
"MyNamespace\\A": [
|
||||||
"./self4.php"
|
"./self4.php"
|
||||||
],
|
],
|
||||||
"MyNamespace\\A->addTestFile()": [
|
"MyNamespace\\A->addTestFile()": [
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"MyNamespace\\A::b()": [
|
"MyNamespace\\A::b()": [
|
||||||
"./static1.php"
|
"./static1.php"
|
||||||
],
|
],
|
||||||
"static": [
|
"MyNamespace\\A": [
|
||||||
"./static1.php"
|
"./static1.php"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"MyNamespace\\A::b()": [
|
"MyNamespace\\A::b()": [
|
||||||
"./static2.php"
|
"./static2.php"
|
||||||
],
|
],
|
||||||
"static": [
|
"MyNamespace\\A": [
|
||||||
"./static2.php"
|
"./static2.php"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
"MyNamespace\\B": [
|
"MyNamespace\\B": [
|
||||||
"./static3.php"
|
"./static3.php"
|
||||||
],
|
],
|
||||||
"MyNamespace\\b()": [
|
"static->b()": [
|
||||||
"./static3.php"
|
"./static3.php"
|
||||||
],
|
],
|
||||||
"b()": [
|
"MyNamespace\\A": [
|
||||||
"./static3.php"
|
"./static3.php"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
"references": {
|
"references": {
|
||||||
"MyNamespace\\B": [
|
"MyNamespace\\B": [
|
||||||
"./static4.php"
|
"./static4.php"
|
||||||
|
],
|
||||||
|
"MyNamespace\\A": [
|
||||||
|
"./static4.php"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
|
|
Loading…
Reference in New Issue