1
0
Fork 0

Cache hover

pull/155/head
Felix Becker 2016-11-17 23:25:01 +01:00
parent 3c00a349a4
commit 279e2fb996
4 changed files with 86 additions and 58 deletions

View File

@ -45,4 +45,18 @@ class Definition
* @var \phpDocumentor\Type|null
*/
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;
}

View File

@ -4,6 +4,7 @@ declare(strict_types = 1);
namespace LanguageServer;
use PhpParser\Node;
use PhpParser\PrettyPrinter\Standard as PrettyPrinter;
use phpDocumentor\Reflection\{Types, Type, Fqsen, TypeResolver};
use LanguageServer\Protocol\SymbolInformation;
use Sabre\Event\Promise;
@ -18,6 +19,61 @@ class DefinitionResolver
{
$this->project = $project;
$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;
// Get symbol information from node (range, symbol kind)
$def->symbolInformation = SymbolInformation::fromNode($defNode);
// Declaration line
$def->declarationLine = $this->getDeclarationLineFromNode($node);
// Documentation
$def->documentation = $this->getDocumentationFromNode($node);
if ($defNode instanceof Node\Param) {
// Get parameter type
$def->type = $this->getTypeFromNode($defNode);

View File

@ -27,7 +27,7 @@ class DefinitionCollector extends NodeVisitorAbstract
*/
public $nodes = [];
public $definitionResolver;
private $definitionResolver;
public function __construct(DefinitionResolver $definitionResolver)
{
@ -46,6 +46,9 @@ class DefinitionCollector extends NodeVisitorAbstract
$def->fqn = $fqn;
$def->symbolInformation = SymbolInformation::fromNode($node, $fqn);
$def->type = $this->definitionResolver->getTypeFromNode($node);
$def->declarationLine = $this->definitionResolver->getDeclarationLineFromNode($node);
$def->documentation = $this->definitionResolver->getDocumentationFromNode($node);
$this->definitions[$fqn] = $def;
}
}

View File

@ -196,66 +196,17 @@ class TextDocument
return new Hover([]);
}
$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
$def = $this->definitionResolver->resolveReferenceNodeToDefinition($node);
if ($def === null) {
return new Hover([], $range);
}
// TODO inefficient. Add documentation and declaration line to Definition class
// so document doesnt have to be loaded
$document = yield $this->project->getOrLoadDocument($def->symbolInformation->location->uri);
if ($document === null) {
return new Hover([], $range);
}
$defNode = $document->getDefinitionNodeByFqn($def->fqn);
// Get the definition for whatever node is under the cursor
$def = $this->definitionResolver->resolveReferenceNodeToDefinition($node);
if ($def === null) {
return new Hover([], $range);
}
$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;
if ($def->declarationLine) {
$contents[] = new MarkedString('php', "<?php\n" . $def->declarationLine);
}
// Don't include the docblock in the declaration string
$defLine->setAttribute('comments', []);
if (isset($defLine->stmts)) {
$defLine->stmts = [];
if ($def->documentation) {
$contents[] = $def->documentation;
}
$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);
});
}