Merge 02057a4baa
into 9dc1656592
commit
9f69c855a4
|
@ -72,6 +72,11 @@ class Definition
|
|||
*/
|
||||
public $symbolInformation;
|
||||
|
||||
/**
|
||||
* @var Location
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* The type a reference to this symbol will resolve to.
|
||||
* For properties and constants, this is the type of the property/constant.
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace LanguageServer;
|
|||
|
||||
use LanguageServer\Index\ReadableIndex;
|
||||
use LanguageServer\Factory\SymbolInformationFactory;
|
||||
use LanguageServer\Factory\LocationFactory;
|
||||
use LanguageServerProtocol\SymbolInformation;
|
||||
use Microsoft\PhpParser;
|
||||
use Microsoft\PhpParser\Node;
|
||||
|
@ -236,6 +237,13 @@ class DefinitionResolver
|
|||
|
||||
$def->symbolInformation = SymbolInformationFactory::fromNode($node, $fqn);
|
||||
|
||||
if ($node instanceof Node\Statement\ClassDeclaration ||
|
||||
$node instanceof Node\MethodDeclaration) {
|
||||
$def->name = LocationFactory::fromToken($node, $node->name);
|
||||
} elseif ($node instanceof Node\Expression\Variable) {
|
||||
$def->name = LocationFactory::fromToken($node, $node->name);
|
||||
$def->name->range->start->character++;
|
||||
}
|
||||
if ($def->symbolInformation !== null) {
|
||||
$def->type = $this->getTypeFromNode($node);
|
||||
$def->declarationLine = $this->getDeclarationLineFromNode($node);
|
||||
|
|
|
@ -6,6 +6,7 @@ use LanguageServerProtocol\Location;
|
|||
use LanguageServerProtocol\Position;
|
||||
use LanguageServerProtocol\Range;
|
||||
use Microsoft\PhpParser\Node;
|
||||
use Microsoft\PhpParser\Token;
|
||||
use Microsoft\PhpParser\PositionUtilities;
|
||||
|
||||
class LocationFactory
|
||||
|
@ -29,4 +30,24 @@ class LocationFactory
|
|||
new Position($range->end->line, $range->end->character)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the location of the token
|
||||
*
|
||||
* @param Token $node
|
||||
* @return self
|
||||
*/
|
||||
public static function fromToken(Node $node, Token $token): Location
|
||||
{
|
||||
$range = PositionUtilities::getRangeFromPosition(
|
||||
$token->getStartPosition(),
|
||||
$token->getWidth(),
|
||||
$node->getFileContents()
|
||||
);
|
||||
|
||||
return new Location($node->getUri(), new Range(
|
||||
new Position($range->start->line, $range->start->character),
|
||||
new Position($range->end->line, $range->end->character)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -423,6 +423,9 @@ class Index implements ReadableIndex, \Serializable
|
|||
*/
|
||||
private function removeIndexedDefinition(int $level, array $parts, array &$storage, array &$rootStorage)
|
||||
{
|
||||
if (empty($parts)) {
|
||||
return;
|
||||
}
|
||||
$part = $parts[$level];
|
||||
|
||||
if ($level + 1 === count($parts)) {
|
||||
|
|
|
@ -4,7 +4,7 @@ declare(strict_types = 1);
|
|||
namespace LanguageServer\Server;
|
||||
|
||||
use LanguageServer\{
|
||||
CompletionProvider, SignatureHelpProvider, LanguageClient, PhpDocument, PhpDocumentLoader, DefinitionResolver
|
||||
CompletionProvider, SignatureHelpProvider, LanguageClient, PhpDocument, PhpDocumentLoader, DefinitionResolver, Definition
|
||||
};
|
||||
use LanguageServer\Index\ReadableIndex;
|
||||
use LanguageServer\Factory\LocationFactory;
|
||||
|
@ -23,6 +23,8 @@ use LanguageServerProtocol\{
|
|||
TextDocumentIdentifier,
|
||||
TextDocumentItem,
|
||||
VersionedTextDocumentIdentifier,
|
||||
WorkspaceEdit,
|
||||
TextEdit,
|
||||
CompletionContext
|
||||
};
|
||||
use Microsoft\PhpParser\Node;
|
||||
|
@ -179,7 +181,7 @@ class TextDocument
|
|||
TextDocumentIdentifier $textDocument,
|
||||
Position $position
|
||||
): Promise {
|
||||
return coroutine(function () use ($textDocument, $position) {
|
||||
return coroutine(function () use ($context, $textDocument, $position) {
|
||||
$document = yield $this->documentLoader->getOrLoad($textDocument->uri);
|
||||
$node = $document->getNodeAtPosition($position);
|
||||
if ($node === null) {
|
||||
|
@ -190,7 +192,7 @@ class TextDocument
|
|||
// by traversing the AST
|
||||
if (
|
||||
|
||||
($node instanceof Node\Expression\Variable && !($node->getParent()->getParent() instanceof Node\PropertyDeclaration))
|
||||
($node instanceof Node\Expression\Variable && !($node->getParent()->getParent() instanceof Node\PropertyDeclaration) && !($node->getParent()->getParent()->getParent() instanceof Node\PropertyDeclaration))
|
||||
|| $node instanceof Node\Parameter
|
||||
|| $node instanceof Node\UseVariableName
|
||||
) {
|
||||
|
@ -210,7 +212,14 @@ class TextDocument
|
|||
if ($descendantNode instanceof Node\Expression\Variable &&
|
||||
$descendantNode->getName() === $node->getName()
|
||||
) {
|
||||
$locations[] = LocationFactory::fromNode($descendantNode);
|
||||
$location = LocationFactory::fromNode($descendantNode);
|
||||
$location->range->start->character++;
|
||||
$locations[] = $location;
|
||||
} else if (($descendantNode instanceof Node\Parameter)
|
||||
&& $context->includeDeclaration && $descendantNode->getName() === $node->getName() ) {
|
||||
$location = LocationFactory::fromToken($descendantNode, $descendantNode->variableName);
|
||||
$location->range->start->character++;
|
||||
$locations[] = $location;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -227,6 +236,10 @@ class TextDocument
|
|||
return [];
|
||||
}
|
||||
}
|
||||
$nameParts = preg_split('/[\>\\\:]/', $fqn);
|
||||
$name = end($nameParts);
|
||||
$nameParts = explode('(', $name);
|
||||
$name = $nameParts[0];
|
||||
$refDocumentPromises = [];
|
||||
foreach ($this->index->getReferenceUris($fqn) as $uri) {
|
||||
$refDocumentPromises[] = $this->documentLoader->getOrLoad($uri);
|
||||
|
@ -236,15 +249,75 @@ class TextDocument
|
|||
$refs = $document->getReferenceNodesByFqn($fqn);
|
||||
if ($refs !== null) {
|
||||
foreach ($refs as $ref) {
|
||||
$locations[] = LocationFactory::fromNode($ref);
|
||||
if ($ref instanceof Node\Expression\MemberAccessExpression) {
|
||||
$locations[] = LocationFactory::fromToken($ref, $ref -> memberName);
|
||||
} else {
|
||||
$locations[] = LocationFactory::fromNode($ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if ($context->includeDeclaration) {
|
||||
$definitionObjects = yield $this->definitionObject($textDocument, $position);
|
||||
$definitionLocations = $definitionObjects->name;
|
||||
if (gettype($definitionLocations) == "string") {
|
||||
throw new \Exception($definitionLocations);
|
||||
}
|
||||
if (!is_array($definitionLocations)) {
|
||||
$definitionLocations = array($definitionLocations);
|
||||
}
|
||||
$locations = array_merge($locations, $definitionLocations);
|
||||
}
|
||||
}
|
||||
return $locations;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The rename request is sent from the client to the server to perform a workspace-wide rename of a symbol.
|
||||
*
|
||||
* @param TextDocumentIdentifier $textDocument The document to rename in.
|
||||
* @param Position $position The position at which this request was sent.
|
||||
* @param string $newName The new name of the symbol.
|
||||
* @return Promise <WorkspaceEdit|null>
|
||||
*/
|
||||
public function rename(
|
||||
TextDocumentIdentifier $textDocument,
|
||||
Position $position,
|
||||
string $newName
|
||||
) : Promise {
|
||||
return coroutine(function () use ($textDocument, $position, $newName) {
|
||||
$document = yield $this->documentLoader->getOrLoad($textDocument->uri);
|
||||
$node = $document->getNodeAtPosition($position);
|
||||
$locations = yield $this->references(new ReferenceContext(true), $textDocument, $position);
|
||||
$edits = [$textDocument->uri => [] ];
|
||||
foreach ($locations as $location) {
|
||||
$textEdit = new TextEdit($location->range, $newName);
|
||||
if (!isset($edits[$location->uri])) {
|
||||
$edits[$location->uri] = [];
|
||||
}
|
||||
$edits[$location->uri][] = $textEdit;
|
||||
}
|
||||
|
||||
foreach ($edits as $uri => $textEdits) {
|
||||
$document = yield $this->documentLoader->getOrLoad($uri);
|
||||
$newtext = $document->getContent();
|
||||
foreach ($textEdits as $textEdit) {
|
||||
$startOffset = $textEdit->range->start->toOffset($document->getContent());
|
||||
$endOffset = $textEdit->range->end->toOffset($document->getContent());
|
||||
$length = $endOffset - $startOffset;
|
||||
$newtext = substr_replace($newtext, $textEdit->newText, $startOffset, $length);
|
||||
}
|
||||
$document->updateContent($newtext);
|
||||
$this->client->textDocument->publishDiagnostics($uri, $document->getDiagnostics());
|
||||
|
||||
}
|
||||
return new WorkspaceEdit($edits);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The signature help request is sent from the client to the server to request signature information at a given
|
||||
* cursor position.
|
||||
|
@ -271,6 +344,21 @@ class TextDocument
|
|||
* @return Promise <Location|Location[]>
|
||||
*/
|
||||
public function definition(TextDocumentIdentifier $textDocument, Position $position): Promise
|
||||
{
|
||||
return coroutine(function () use ($textDocument, $position) {
|
||||
$def = yield $this->definitionObject($textDocument, $position);
|
||||
if (
|
||||
$def === null
|
||||
|| $def->symbolInformation === null
|
||||
|| Uri\parse($def->symbolInformation->location->uri)['scheme'] === 'phpstubs'
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
return $def->symbolInformation->location;
|
||||
});
|
||||
}
|
||||
|
||||
private function definitionObject(TextDocumentIdentifier $textDocument, Position $position)
|
||||
{
|
||||
return coroutine(function () use ($textDocument, $position) {
|
||||
$document = yield $this->documentLoader->getOrLoad($textDocument->uri);
|
||||
|
@ -293,14 +381,7 @@ class TextDocument
|
|||
}
|
||||
yield waitForEvent($this->index, 'definition-added');
|
||||
}
|
||||
if (
|
||||
$def === null
|
||||
|| $def->symbolInformation === null
|
||||
|| Uri\parse($def->symbolInformation->location->uri)['scheme'] === 'phpstubs'
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
return $def->symbolInformation->location;
|
||||
return $def;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue