Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/Definition.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 8 additions & 0 deletions src/DefinitionResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

use LanguageServer\Index\ReadableIndex;
use LanguageServer\Factory\SymbolInformationFactory;
use LanguageServer\Factory\LocationFactory;
use LanguageServerProtocol\SymbolInformation;
use Microsoft\PhpParser;
use Microsoft\PhpParser\Node;
Expand Down Expand Up @@ -236,6 +237,13 @@ public function createDefinitionFromNode(Node $node, string $fqn = null): Defini

$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);
Expand Down
21 changes: 21 additions & 0 deletions src/Factory/LocationFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use LanguageServerProtocol\Position;
use LanguageServerProtocol\Range;
use Microsoft\PhpParser\Node;
use Microsoft\PhpParser\Token;
use Microsoft\PhpParser\PositionUtilities;

class LocationFactory
Expand All @@ -29,4 +30,24 @@ public static function fromNode(Node $node): Location
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)
));
}
}
6 changes: 6 additions & 0 deletions src/Index/Index.php
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@ public function setDefinition(string $fqn, Definition $definition)
public function removeDefinition(string $fqn)
{
$parts = $this->splitFqn($fqn);
if (empty($parts)) {
throw new \Exception($fqn);
}
$this->removeIndexedDefinition(0, $parts, $this->definitions, $this->definitions);

unset($this->references[$fqn]);
Expand Down Expand Up @@ -423,6 +426,9 @@ private function indexDefinition(int $level, array $parts, array &$storage, Defi
*/
private function removeIndexedDefinition(int $level, array $parts, array &$storage, array &$rootStorage)
{
if (empty($parts)) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really understand this function well enough to be confident about this code, but it fixes an exception that I got before and it seems intuitively right to have a base case for the recursion.

return;
}
$part = $parts[$level];

if ($level + 1 === count($parts)) {
Expand Down
107 changes: 94 additions & 13 deletions src/Server/TextDocument.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
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;
Expand All @@ -23,6 +23,8 @@
TextDocumentIdentifier,
TextDocumentItem,
VersionedTextDocumentIdentifier,
WorkspaceEdit,
TextEdit,
CompletionContext
};
use Microsoft\PhpParser\Node;
Expand Down Expand Up @@ -179,7 +181,7 @@ public function references(
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) {
Expand All @@ -190,7 +192,7 @@ public function references(
// 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
) {
Expand All @@ -210,7 +212,14 @@ public function references(
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 {
Expand All @@ -227,6 +236,10 @@ public function references(
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);
Expand All @@ -236,15 +249,75 @@ public function references(
$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.
Expand All @@ -271,6 +344,21 @@ public function signatureHelp(TextDocumentIdentifier $textDocument, Position $po
* @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);
Expand All @@ -293,14 +381,7 @@ public function definition(TextDocumentIdentifier $textDocument, Position $posit
}
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;
});
}

Expand Down