1
0
Fork 0

Add Index class

pull/214/head
Felix Becker 2016-12-07 08:36:33 +01:00
parent 10fb3c92e0
commit 347dd14b20
3 changed files with 190 additions and 14 deletions

164
src/Index.php Normal file
View File

@ -0,0 +1,164 @@
<?php
declare(strict_types = 1);
namespace LanguageServer;
use LanguageServer\Protocol\{SymbolInformation, TextDocumentIdentifier, ClientCapabilities};
use phpDocumentor\Reflection\DocBlockFactory;
use Sabre\Event\Promise;
use function Sabre\Event\coroutine;
/**
* Represents the index of a project or dependency
* Serializable for caching
*/
class Index
{
/**
* An associative array that maps fully qualified symbol names to Definitions
*
* @var Definition[]
*/
private $definitions = [];
/**
* An associative array that maps fully qualified symbol names to arrays of document URIs that reference the symbol
*
* @var string[][]
*/
private $references = [];
/**
* Returns an associative array [string => Definition] that maps fully qualified symbol names
* to Definitions
*
* @return Definitions[]
*/
public function getDefinitions()
{
return $this->definitions;
}
/**
* Returns the Definition object by a specific FQN
*
* @param string $fqn
* @param bool $globalFallback Whether to fallback to global if the namespaced FQN was not found
* @return Definition|null
*/
public function getDefinition(string $fqn, $globalFallback = false)
{
if (isset($this->definitions[$fqn])) {
return $this->definitions[$fqn];
} else if ($globalFallback) {
$parts = explode('\\', $fqn);
$fqn = end($parts);
return $this->getDefinition($fqn);
}
}
/**
* Registers a definition
*
* @param string $fqn The fully qualified name of the symbol
* @param string $definition The Definition object
* @return void
*/
public function setDefinition(string $fqn, Definition $definition)
{
$this->definitions[$fqn] = $definition;
}
/**
* Sets the Definition index
*
* @param Definition[] $definitions Map from FQN to Definition
* @return void
*/
public function setDefinitions(array $definitions)
{
$this->definitions = $definitions;
}
/**
* Unsets the Definition for a specific symbol
* and removes all references pointing to that symbol
*
* @param string $fqn The fully qualified name of the symbol
* @return void
*/
public function removeDefinition(string $fqn)
{
unset($this->definitions[$fqn]);
unset($this->references[$fqn]);
}
/**
* Adds a document URI as a referencee of a specific symbol
*
* @param string $fqn The fully qualified name of the symbol
* @return void
*/
public function addReferenceUri(string $fqn, string $uri)
{
if (!isset($this->references[$fqn])) {
$this->references[$fqn] = [];
}
// TODO: use DS\Set instead of searching array
if (array_search($uri, $this->references[$fqn], true) === false) {
$this->references[$fqn][] = $uri;
}
}
/**
* Removes a document URI as the container for a specific symbol
*
* @param string $fqn The fully qualified name of the symbol
* @param string $uri The URI
* @return void
*/
public function removeReferenceUri(string $fqn, string $uri)
{
if (!isset($this->references[$fqn])) {
return;
}
$index = array_search($fqn, $this->references[$fqn], true);
if ($index === false) {
return;
}
array_splice($this->references[$fqn], $index, 1);
}
/**
* Returns an associative array [string => string[]] that maps fully qualified symbol names
* to URIs of the document where the symbol is referenced
*
* @return string[][]
*/
public function getReferenceUris()
{
return $this->references;
}
/**
* Sets the reference index
*
* @param string[][] $references an associative array [string => string[]] from FQN to URIs
* @return void
*/
public function setReferenceUris(array $references)
{
$this->references = $references;
}
/**
* Returns true if the given FQN is defined in the project
*
* @param string $fqn The fully qualified name of the symbol
* @return bool
*/
public function isDefined(string $fqn): bool
{
return isset($this->definitions[$fqn]);
}
}

View File

@ -183,29 +183,33 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher
private function indexProject(): Promise
{
return coroutine(function () {
$textDocuments = yield $this->findPhpFiles();
$count = count($textDocuments);
$pattern = Path::makeAbsolute('**/{*.php,composer.lock}', $this->rootPath);
$phpPattern = Path::makeAbsolute('**/*.php', $this->rootPath);
$composerLockPattern = Path::makeAbsolute('**/composer.lock', $this->rootPath);
$uris = yield $this->findFiles($pattern);
$count = count($uris);
$startTime = microtime(true);
foreach ($textDocuments as $i => $textDocument) {
foreach ($uris as $i => $uri) {
// Give LS to the chance to handle requests while indexing
yield timeout();
if (Glob::match())
$this->client->window->logMessage(
MessageType::LOG,
"Parsing file $i/$count: {$textDocument->uri}"
"Parsing file $i/$count: {$uri}"
);
try {
yield $this->project->loadDocument($textDocument->uri);
yield $this->project->loadDocument($uri);
} catch (ContentTooLargeException $e) {
$this->client->window->logMessage(
MessageType::INFO,
"Ignoring file {$textDocument->uri} because it exceeds size limit of {$e->limit} bytes ({$e->size})"
"Ignoring file {$uri} because it exceeds size limit of {$e->limit} bytes ({$e->size})"
);
} catch (Exception $e) {
$this->client->window->logMessage(
MessageType::ERROR,
"Error parsing file {$textDocument->uri}: " . (string)$e
"Error parsing file {$uri}: " . (string)$e
);
}
}
@ -223,29 +227,29 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher
* Returns all PHP files in the workspace.
* If the client does not support workspace/files, it falls back to searching the file system directly.
*
* @return Promise <TextDocumentIdentifier[]>
* @param string $pattern
* @return Promise <string[]>
*/
private function findPhpFiles(): Promise
private function findFiles(string $pattern): Promise
{
return coroutine(function () {
$textDocuments = [];
$pattern = Path::makeAbsolute('**/*.php', $this->rootPath);
$uris = [];
if ($this->clientCapabilities->xfilesProvider) {
// Use xfiles request
foreach (yield $this->client->workspace->xfiles() as $textDocument) {
$path = Uri\parse($textDocument->uri)['path'];
if (Glob::match($path, $pattern)) {
$textDocuments[] = $textDocument;
$uris[] = $textDocument->uri;
}
}
} else {
// Use the file system
foreach (new GlobIterator($pattern) as $path) {
$textDocuments[] = new TextDocumentIdentifier(pathToUri($path));
$uris[] = pathToUri($path);
yield timeout();
}
}
return $textDocuments;
return $uris;
});
}
}

View File

@ -18,6 +18,14 @@ class Project
*/
private $documents = [];
/**
* Associative array from package identifier to index
* The empty string represents the project itself
*
* @var Index[]
*/
private $indexes = [];
/**
* An associative array that maps fully qualified symbol names to Definitions
*