1
0
Fork 0
php-language-server/src/NodeVisitors/SymbolFinder.php

122 lines
3.6 KiB
PHP
Raw Normal View History

2016-08-25 13:27:14 +00:00
<?php
declare(strict_types = 1);
2016-10-08 10:59:22 +00:00
namespace LanguageServer\NodeVisitors;
2016-08-25 13:27:14 +00:00
use PhpParser\{NodeVisitorAbstract, Node};
2016-09-02 00:56:45 +00:00
use LanguageServer\Protocol\{SymbolInformation, SymbolKind, Range, Position, Location};
2016-08-25 13:27:14 +00:00
class SymbolFinder extends NodeVisitorAbstract
{
const NODE_SYMBOL_KIND_MAP = [
Node\Stmt\Class_::class => SymbolKind::CLASS_,
Node\Stmt\Trait_::class => SymbolKind::CLASS_,
2016-08-25 13:27:14 +00:00
Node\Stmt\Interface_::class => SymbolKind::INTERFACE,
Node\Stmt\Namespace_::class => SymbolKind::NAMESPACE,
Node\Stmt\Function_::class => SymbolKind::FUNCTION,
Node\Stmt\ClassMethod::class => SymbolKind::METHOD,
Node\Stmt\PropertyProperty::class => SymbolKind::PROPERTY,
Node\Const_::class => SymbolKind::CONSTANT,
Node\Expr\Variable::class => SymbolKind::VARIABLE
];
/**
* @var LanguageServer\Protocol\SymbolInformation[]
*/
2016-09-09 17:57:28 +00:00
public $symbols = [];
2016-08-25 13:27:14 +00:00
/**
* @var string
*/
private $uri;
/**
* @var string
*/
private $containerName;
/**
* @var array
*/
private $nameStack = [];
/**
* @var array
*/
private $nodeStack = [];
/**
* @var int
*/
private $functionCount = 0;
2016-08-25 13:27:14 +00:00
public function __construct(string $uri)
{
$this->uri = $uri;
}
public function enterNode(Node $node)
{
$this->nodeStack[] = $node;
$containerName = end($this->nameStack);
// If we enter a named node, push its name onto name stack.
// Else push the current name onto stack.
if (!empty($node->name) && (is_string($node->name) || method_exists($node->name, '__toString')) && !empty((string)$node->name)) {
if (empty($containerName)) {
$this->nameStack[] = (string)$node->name;
} else if ($node instanceof Node\Stmt\ClassMethod) {
$this->nameStack[] = $containerName . '::' . (string)$node->name;
} else {
$this->nameStack[] = $containerName . '\\' . (string)$node->name;
}
} else {
$this->nameStack[] = $containerName;
// We are not interested in unnamed nodes, return
return;
}
2016-08-25 13:27:14 +00:00
$class = get_class($node);
2016-09-02 00:56:45 +00:00
if (!isset(self::NODE_SYMBOL_KIND_MAP[$class])) {
2016-08-25 13:27:14 +00:00
return;
}
2016-09-09 17:57:28 +00:00
// if we enter a method or function, increase the function counter
if ($node instanceof Node\Stmt\Function_ || $node instanceof Node\Stmt\ClassMethod) {
$this->functionCount++;
}
2016-09-09 17:57:28 +00:00
$kind = self::NODE_SYMBOL_KIND_MAP[$class];
// exclude non-global variable symbols.
if ($kind === SymbolKind::VARIABLE && $this->functionCount > 0) {
return;
2016-09-09 17:57:28 +00:00
}
2016-08-25 13:27:14 +00:00
$symbol = new SymbolInformation();
2016-09-09 17:57:28 +00:00
$symbol->kind = $kind;
2016-08-25 13:27:14 +00:00
$symbol->name = (string)$node->name;
$symbol->location = new Location(
$this->uri,
new Range(
2016-09-02 00:56:45 +00:00
new Position($node->getAttribute('startLine') - 1, $node->getAttribute('startColumn') - 1),
new Position($node->getAttribute('endLine') - 1, $node->getAttribute('endColumn'))
2016-08-25 13:27:14 +00:00
)
);
$symbol->containerName = $containerName;
2016-08-25 13:27:14 +00:00
$this->symbols[] = $symbol;
}
public function leaveNode(Node $node)
{
array_pop($this->nodeStack);
array_pop($this->nameStack);
// if we leave a method or function, decrease the function counter
if ($node instanceof Node\Stmt\Function_ || $node instanceof Node\Stmt\ClassMethod) {
$this->functionCount--;
}
2016-08-25 13:27:14 +00:00
}
}