From 2d91ee8eff807bd4c48168cc0c6eb9f1bda13415 Mon Sep 17 00:00:00 2001 From: Nicolas MURE Date: Sun, 6 Aug 2017 16:48:41 +0200 Subject: [PATCH] speedup autocomplete with ReadableIndex::getDefinitionsForNamespace --- src/CompletionProvider.php | 35 +++++++++--- src/Index/AbstractAggregateIndex.php | 17 ++++++ src/Index/Index.php | 79 ++++++++++++++++++++++++++++ src/Index/ReadableIndex.php | 8 +++ 4 files changed, 133 insertions(+), 6 deletions(-) diff --git a/src/CompletionProvider.php b/src/CompletionProvider.php index 68c5a0c..ae4f958 100644 --- a/src/CompletionProvider.php +++ b/src/CompletionProvider.php @@ -201,21 +201,44 @@ class CompletionProvider $this->definitionResolver->resolveExpressionNodeToType($node->dereferencableExpression) ); + $checksCount = 0; + $start = microtime(true); // Add the object access operator to only get members of all parents - $prefixes = []; - foreach ($this->expandParentFqns($fqns) as $prefix) { - $prefixes[] = $prefix . '->'; + $namespaces = []; + foreach ($this->expandParentFqns($fqns) as $namespace) { + $namespaces[] = $namespace; } // Collect all definitions that match any of the prefixes - foreach ($this->index->getDefinitions() as $fqn => $def) { - foreach ($prefixes as $prefix) { - if (substr($fqn, 0, strlen($prefix)) === $prefix && $def->isMember) { + foreach ($namespaces as $namespace) { + foreach ($this->index->getDefinitionsForNamespace($namespace) as $fqn => $def) { + ++$checksCount; + $prefix = $namespace . '->'; + if (substr($fqn, 0, strlen($prefix)) === $prefix && !$def->isMember) { $list->items[] = CompletionItem::fromDefinition($def); } } } + // $prefixes = []; + // foreach ($this->expandParentFqns($fqns) as $prefix) { + // $prefixes[] = $prefix . '->'; + // } + // foreach ($this->index->getDefinitions() as $fqn => $def) { + // foreach ($prefixes as $prefix) { + // ++$checksCount; + // if (substr($fqn, 0, strlen($prefix)) === $prefix && !$def->isGlobal) { + // $list->items[] = CompletionItem::fromDefinition($def); + // } + // } + // } + $duration = microtime(true) - $start; + file_put_contents( + '/home/nicolas/tmp/php_language-server.log', + sprintf("%d items found, %d checks, memory : %d bytes, %ss\n", sizeof($list->items), $checksCount, memory_get_usage(true), $duration), + FILE_APPEND + ); + } elseif ( ($scoped = $node->parent) instanceof Node\Expression\ScopedPropertyAccessExpression || ($scoped = $node) instanceof Node\Expression\ScopedPropertyAccessExpression diff --git a/src/Index/AbstractAggregateIndex.php b/src/Index/AbstractAggregateIndex.php index 5377c3a..fa13d14 100644 --- a/src/Index/AbstractAggregateIndex.php +++ b/src/Index/AbstractAggregateIndex.php @@ -115,6 +115,23 @@ abstract class AbstractAggregateIndex implements ReadableIndex return $defs; } + /** + * Returns the Definitions that are in the given namespace + * + * @param string $namespace + * @return Definitions[] + */ + public function getDefinitionsForNamespace(string $namespace): array + { + $defs = []; + foreach ($this->getIndexes() as $index) { + foreach ($index->getDefinitionsForNamespace($namespace) as $fqn => $def) { + $defs[$fqn] = $def; + } + } + return $defs; + } + /** * Returns the Definition object by a specific FQN * diff --git a/src/Index/Index.php b/src/Index/Index.php index 9cb975e..8af0688 100644 --- a/src/Index/Index.php +++ b/src/Index/Index.php @@ -21,6 +21,13 @@ class Index implements ReadableIndex, \Serializable */ private $definitions = []; + /** + * An associative array that maps namespaces to an associative array of FQN to Definitions + * + * @var Definition[] + */ + private $namespaceDefinitions = []; + /** * An associative array that maps fully qualified symbol names to arrays of document URIs that reference the symbol * @@ -94,6 +101,20 @@ class Index implements ReadableIndex, \Serializable return $this->definitions; } + /** + * Returns the Definitions that are in the given namespace + * + * @param string $namespace + * @return Definitions[] + */ + public function getDefinitionsForNamespace(string $namespace): array + { + return isset($this->namespaceDefinitions[$namespace]) + ? $this->namespaceDefinitions[$namespace] + : [] + ; + } + /** * Returns the Definition object by a specific FQN * @@ -123,6 +144,7 @@ class Index implements ReadableIndex, \Serializable public function setDefinition(string $fqn, Definition $definition) { $this->definitions[$fqn] = $definition; + $this->setNamespaceDefinition($fqn, $definition); $this->emit('definition-added'); } @@ -137,6 +159,7 @@ class Index implements ReadableIndex, \Serializable { unset($this->definitions[$fqn]); unset($this->references[$fqn]); + $this->removeNamespaceDefinition($fqn); } /** @@ -207,6 +230,7 @@ class Index implements ReadableIndex, \Serializable foreach ($data as $prop => $val) { $this->$prop = $val; } + $this->buildNamespaceDefinitionsIndex(); } /** @@ -222,4 +246,59 @@ class Index implements ReadableIndex, \Serializable 'staticComplete' => $this->staticComplete ]); } + + /** + * @return void + */ + private function buildNamespaceDefinitionsIndex() + { + foreach ($this->definitions as $fqn => $definition) { + $this->setNamespaceDefinition($fqn, $definition); + } + } + + /** + * Registers a definition to a namespace + * + * @param string $fqn The fully qualified name of the symbol + * @param Definition $definition The Definition object + * @return void + */ + private function setNamespaceDefinition(string $fqn, Definition $definition) + { + $namespace = $this->extractNamespace($fqn); + if (!isset($this->namespaceDefinitions[$namespace])) { + $this->namespaceDefinitions[$namespace] = []; + } + $this->namespaceDefinitions[$namespace][$fqn] = $definition; + } + + /** + * Removes a definition from a namespace + * + * @param string $fqn The fully qualified name of the symbol + * @return void + */ + private function removeNamespaceDefinition(string $fqn) + { + $namespace = $this->extractNamespace($fqn); + if (isset($this->namespaceDefinitions[$namespace])) { + unset($this->namespaceDefinitions[$namespace][$fqn]); + } + } + + /** + * @param string $fqn + * @return string The namespace extracted from the given FQN + */ + private function extractNamespace(string $fqn): string + { + foreach (['::', '->'] as $operator) { + if (false !== ($pos = strpos($fqn, $operator))) { + return substr($fqn, 0, $pos); + } + } + + return $fqn; + } } diff --git a/src/Index/ReadableIndex.php b/src/Index/ReadableIndex.php index 67b20b6..c7d5732 100644 --- a/src/Index/ReadableIndex.php +++ b/src/Index/ReadableIndex.php @@ -37,6 +37,14 @@ interface ReadableIndex extends EmitterInterface */ public function getDefinitions(): array; + /** + * Returns the Definitions that are in the given namespace + * + * @param string $namespace + * @return Definitions[] + */ + public function getDefinitionsForNamespace(string $namespace): array; + /** * Returns the Definition object by a specific FQN *