From 347dd14b20512ee87d6ad87d9509cc480c3b67e7 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Wed, 7 Dec 2016 08:36:33 +0100 Subject: [PATCH] Add Index class --- src/Index.php | 164 +++++++++++++++++++++++++++++++++++++++++ src/LanguageServer.php | 32 ++++---- src/Project.php | 8 ++ 3 files changed, 190 insertions(+), 14 deletions(-) create mode 100644 src/Index.php diff --git a/src/Index.php b/src/Index.php new file mode 100644 index 0000000..baa3a48 --- /dev/null +++ b/src/Index.php @@ -0,0 +1,164 @@ + 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]); + } +} diff --git a/src/LanguageServer.php b/src/LanguageServer.php index 42a1b64..43558ba 100644 --- a/src/LanguageServer.php +++ b/src/LanguageServer.php @@ -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 + * @param string $pattern + * @return Promise */ - 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; }); } } diff --git a/src/Project.php b/src/Project.php index 3021a1c..b9802d3 100644 --- a/src/Project.php +++ b/src/Project.php @@ -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 *