Cache hover
parent
3c00a349a4
commit
279e2fb996
|
@ -45,4 +45,18 @@ class Definition
|
||||||
* @var \phpDocumentor\Type|null
|
* @var \phpDocumentor\Type|null
|
||||||
*/
|
*/
|
||||||
public $type;
|
public $type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The first line of the declaration, for use in textDocument/hover
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $declarationLine;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A documentation string, for use in textDocument/hover
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $documentation;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ declare(strict_types = 1);
|
||||||
namespace LanguageServer;
|
namespace LanguageServer;
|
||||||
|
|
||||||
use PhpParser\Node;
|
use PhpParser\Node;
|
||||||
|
use PhpParser\PrettyPrinter\Standard as PrettyPrinter;
|
||||||
use phpDocumentor\Reflection\{Types, Type, Fqsen, TypeResolver};
|
use phpDocumentor\Reflection\{Types, Type, Fqsen, TypeResolver};
|
||||||
use LanguageServer\Protocol\SymbolInformation;
|
use LanguageServer\Protocol\SymbolInformation;
|
||||||
use Sabre\Event\Promise;
|
use Sabre\Event\Promise;
|
||||||
|
@ -18,6 +19,61 @@ class DefinitionResolver
|
||||||
{
|
{
|
||||||
$this->project = $project;
|
$this->project = $project;
|
||||||
$this->typeResolver = new TypeResolver;
|
$this->typeResolver = new TypeResolver;
|
||||||
|
$this->prettyPrinter = new PrettyPrinter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the declaration line for a given node
|
||||||
|
*
|
||||||
|
* @param Node $node
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDeclarationLineFromNode(Node $node): string
|
||||||
|
{
|
||||||
|
if ($node instanceof Node\Stmt\PropertyProperty || $node instanceof Node\Const_) {
|
||||||
|
// Properties and constants can have multiple declarations
|
||||||
|
// Use the parent node (that includes the modifiers), but only render the requested declaration
|
||||||
|
$child = $node;
|
||||||
|
$node = $node->getAttribute('parentNode');
|
||||||
|
$defLine = clone $node;
|
||||||
|
$defLine->props = [$child];
|
||||||
|
} else {
|
||||||
|
$defLine = clone $node;
|
||||||
|
}
|
||||||
|
// Don't include the docblock in the declaration string
|
||||||
|
$defLine->setAttribute('comments', []);
|
||||||
|
if (isset($defLine->stmts)) {
|
||||||
|
$defLine->stmts = [];
|
||||||
|
}
|
||||||
|
$defText = $this->prettyPrinter->prettyPrint([$defLine]);
|
||||||
|
return strstr($defText, "\n", true) ?: $defText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the documentation string for a node, if it has one
|
||||||
|
*
|
||||||
|
* @param Node $node
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getDocumentationFromNode(Node $node)
|
||||||
|
{
|
||||||
|
if ($node instanceof Node\Param) {
|
||||||
|
$fn = $node->getAttribute('parentNode');
|
||||||
|
$docBlock = $fn->getAttribute('docBlock');
|
||||||
|
if ($docBlock !== null) {
|
||||||
|
$tags = $docBlock->getTagsByName('param');
|
||||||
|
foreach ($tags as $tag) {
|
||||||
|
if ($tag->getVariableName() === $node->name) {
|
||||||
|
return $tag->getDescription()->render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$docBlock = $node->getAttribute('docBlock');
|
||||||
|
if ($docBlock !== null) {
|
||||||
|
return $docBlock->getSummary();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,6 +91,10 @@ class DefinitionResolver
|
||||||
$def = new Definition;
|
$def = new Definition;
|
||||||
// Get symbol information from node (range, symbol kind)
|
// Get symbol information from node (range, symbol kind)
|
||||||
$def->symbolInformation = SymbolInformation::fromNode($defNode);
|
$def->symbolInformation = SymbolInformation::fromNode($defNode);
|
||||||
|
// Declaration line
|
||||||
|
$def->declarationLine = $this->getDeclarationLineFromNode($node);
|
||||||
|
// Documentation
|
||||||
|
$def->documentation = $this->getDocumentationFromNode($node);
|
||||||
if ($defNode instanceof Node\Param) {
|
if ($defNode instanceof Node\Param) {
|
||||||
// Get parameter type
|
// Get parameter type
|
||||||
$def->type = $this->getTypeFromNode($defNode);
|
$def->type = $this->getTypeFromNode($defNode);
|
||||||
|
|
|
@ -27,7 +27,7 @@ class DefinitionCollector extends NodeVisitorAbstract
|
||||||
*/
|
*/
|
||||||
public $nodes = [];
|
public $nodes = [];
|
||||||
|
|
||||||
public $definitionResolver;
|
private $definitionResolver;
|
||||||
|
|
||||||
public function __construct(DefinitionResolver $definitionResolver)
|
public function __construct(DefinitionResolver $definitionResolver)
|
||||||
{
|
{
|
||||||
|
@ -46,6 +46,9 @@ class DefinitionCollector extends NodeVisitorAbstract
|
||||||
$def->fqn = $fqn;
|
$def->fqn = $fqn;
|
||||||
$def->symbolInformation = SymbolInformation::fromNode($node, $fqn);
|
$def->symbolInformation = SymbolInformation::fromNode($node, $fqn);
|
||||||
$def->type = $this->definitionResolver->getTypeFromNode($node);
|
$def->type = $this->definitionResolver->getTypeFromNode($node);
|
||||||
|
$def->declarationLine = $this->definitionResolver->getDeclarationLineFromNode($node);
|
||||||
|
$def->documentation = $this->definitionResolver->getDocumentationFromNode($node);
|
||||||
|
|
||||||
$this->definitions[$fqn] = $def;
|
$this->definitions[$fqn] = $def;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -196,66 +196,17 @@ class TextDocument
|
||||||
return new Hover([]);
|
return new Hover([]);
|
||||||
}
|
}
|
||||||
$range = Range::fromNode($node);
|
$range = Range::fromNode($node);
|
||||||
if ($node instanceof Node\Expr\Variable) {
|
|
||||||
$defNode = DefinitionResolver::resolveVariableToNode($node);
|
|
||||||
} else {
|
|
||||||
// Get the definition for whatever node is under the cursor
|
// Get the definition for whatever node is under the cursor
|
||||||
$def = $this->definitionResolver->resolveReferenceNodeToDefinition($node);
|
$def = $this->definitionResolver->resolveReferenceNodeToDefinition($node);
|
||||||
if ($def === null) {
|
if ($def === null) {
|
||||||
return new Hover([], $range);
|
return new Hover([], $range);
|
||||||
}
|
}
|
||||||
// TODO inefficient. Add documentation and declaration line to Definition class
|
if ($def->declarationLine) {
|
||||||
// so document doesnt have to be loaded
|
$contents[] = new MarkedString('php', "<?php\n" . $def->declarationLine);
|
||||||
$document = yield $this->project->getOrLoadDocument($def->symbolInformation->location->uri);
|
|
||||||
if ($document === null) {
|
|
||||||
return new Hover([], $range);
|
|
||||||
}
|
}
|
||||||
$defNode = $document->getDefinitionNodeByFqn($def->fqn);
|
if ($def->documentation) {
|
||||||
|
$contents[] = $def->documentation;
|
||||||
}
|
}
|
||||||
$contents = [];
|
|
||||||
|
|
||||||
// Build a declaration string
|
|
||||||
if ($defNode instanceof Node\Stmt\PropertyProperty || $defNode instanceof Node\Const_) {
|
|
||||||
// Properties and constants can have multiple declarations
|
|
||||||
// Use the parent node (that includes the modifiers), but only render the requested declaration
|
|
||||||
$child = $defNode;
|
|
||||||
$defNode = $defNode->getAttribute('parentNode');
|
|
||||||
$defLine = clone $defNode;
|
|
||||||
$defLine->props = [$child];
|
|
||||||
} else {
|
|
||||||
$defLine = clone $defNode;
|
|
||||||
}
|
|
||||||
// Don't include the docblock in the declaration string
|
|
||||||
$defLine->setAttribute('comments', []);
|
|
||||||
if (isset($defLine->stmts)) {
|
|
||||||
$defLine->stmts = [];
|
|
||||||
}
|
|
||||||
$defText = $this->prettyPrinter->prettyPrint([$defLine]);
|
|
||||||
$lines = explode("\n", $defText);
|
|
||||||
if (isset($lines[0])) {
|
|
||||||
$contents[] = new MarkedString('php', "<?php\n" . $lines[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the documentation string
|
|
||||||
if ($defNode instanceof Node\Param) {
|
|
||||||
$fn = $defNode->getAttribute('parentNode');
|
|
||||||
$docBlock = $fn->getAttribute('docBlock');
|
|
||||||
if ($docBlock !== null) {
|
|
||||||
$tags = $docBlock->getTagsByName('param');
|
|
||||||
foreach ($tags as $tag) {
|
|
||||||
if ($tag->getVariableName() === $defNode->name) {
|
|
||||||
$contents[] = $tag->getDescription()->render();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$docBlock = $defNode->getAttribute('docBlock');
|
|
||||||
if ($docBlock !== null) {
|
|
||||||
$contents[] = $docBlock->getSummary();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Hover($contents, $range);
|
return new Hover($contents, $range);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue