From 14ff2c46b0bcf375fce6cda5d5c1f48524e40e56 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sat, 28 Jan 2017 01:07:06 +0100 Subject: [PATCH] WIP --- src/Server/TextDocument.php | 111 +++++++++++++++++------------------- src/Server/Workspace.php | 103 +++++++++++++++++++-------------- 2 files changed, 111 insertions(+), 103 deletions(-) diff --git a/src/Server/TextDocument.php b/src/Server/TextDocument.php index 04c89d1..9f832e3 100644 --- a/src/Server/TextDocument.php +++ b/src/Server/TextDocument.php @@ -171,11 +171,11 @@ class TextDocument * * @param TextDocumentIdentifier $textDocument The document to format * @param FormattingOptions $options The format options - * @return Promise + * @return Observable */ public function formatting(TextDocumentIdentifier $textDocument, FormattingOptions $options) { - return $this->documentLoader->getOrLoad($textDocument->uri)->then(function (PhpDocument $document) { + $this->documentLoader->getOrLoad($textDocument->uri)->map(function (PhpDocument $document) { return $document->getFormattedText(); }); } @@ -185,73 +185,66 @@ class TextDocument * denoted by the given text document position. * * @param ReferenceContext $context - * @return Promise + * @return Observable Emits JSON Patch operations that eventually result in Location[] */ public function references( ReferenceContext $context, TextDocumentIdentifier $textDocument, Position $position - ): Promise { - return coroutine(function () use ($textDocument, $position) { - $document = yield $this->documentLoader->getOrLoad($textDocument->uri); - $node = $document->getNodeAtPosition($position); - if ($node === null) { - return []; - } - $locations = []; - // Variables always stay in the boundary of the file and need to be searched inside their function scope - // by traversing the AST - if ( - $node instanceof Node\Expr\Variable - || $node instanceof Node\Param - || $node instanceof Node\Expr\ClosureUse - ) { - if ($node->name instanceof Node\Expr) { - return null; + ): Observable { + return $this->documentLoader->getOrLoad($textDocument->uri) + ->flatMap(function (PhpDocument $document) { + $node = $document->getNodeAtPosition($position); + if ($node === null) { + return Observable::empty(); } - // Find function/method/closure scope - $n = $node; - while (isset($n) && !($n instanceof Node\FunctionLike)) { - $n = $n->getAttribute('parentNode'); - } - if (!isset($n)) { - $n = $node->getAttribute('ownerDocument'); - } - $traverser = new NodeTraverser; - $refCollector = new VariableReferencesCollector($node->name); - $traverser->addVisitor($refCollector); - $traverser->traverse($n->getStmts()); - foreach ($refCollector->nodes as $ref) { - $locations[] = Location::fromNode($ref); - } - } else { - // Definition with a global FQN - $fqn = DefinitionResolver::getDefinedFqn($node); - // Wait until indexing finished - if (!$this->index->isComplete()) { - yield waitForEvent($this->index, 'complete'); - } - if ($fqn === null) { - $fqn = $this->definitionResolver->resolveReferenceNodeToFqn($node); - if ($fqn === null) { - return []; + // Variables always stay in the boundary of the file and need to be searched inside their function scope + // by traversing the AST + if ( + $node instanceof Node\Expr\Variable + || $node instanceof Node\Param + || $node instanceof Node\Expr\ClosureUse + ) { + if ($node->name instanceof Node\Expr) { + return Observable::empty(); } - } - $refDocuments = yield Promise\all(array_map( - [$this->documentLoader, 'getOrLoad'], - $this->index->getReferenceUris($fqn) - )); - foreach ($refDocuments as $document) { - $refs = $document->getReferenceNodesByFqn($fqn); - if ($refs !== null) { - foreach ($refs as $ref) { - $locations[] = Location::fromNode($ref); + // Find function/method/closure scope + $n = $node; + while (isset($n) && !($n instanceof Node\FunctionLike)) { + $n = $n->getAttribute('parentNode'); + } + if (!isset($n)) { + $n = $node->getAttribute('ownerDocument'); + } + $traverser = new NodeTraverser; + $refCollector = new VariableReferencesCollector($node->name); + $traverser->addVisitor($refCollector); + $traverser->traverse($n->getStmts()); + return Observable::fromArray($refCollector->nodes); + } else { + // Definition with a global FQN + $fqn = DefinitionResolver::getDefinedFqn($node); + // Wait until indexing finished + if (!$this->index->isComplete()) { + yield waitForEvent($this->index, 'complete'); + } + if ($fqn === null) { + $fqn = $this->definitionResolver->resolveReferenceNodeToFqn($node); + if ($fqn === null) { + return Observable::empty(); } } + return Observable::fromArray($this->index->getReferenceUris($fqn)) + ->flatMap([$this->documentLoader, 'getOrLoad']) + ->flatMap(function (PhpDocument $document) { + return Observable::from($document->getReferenceNodesByFqn($fqn)); + }); } - } - return $locations; - }); + }) + ->map(function (Node $node) { + return new Operation\Add('/-', Location::fromNode($node)); + }) + ->startWith(new Operation\Replace('/', [])); } /** diff --git a/src/Server/Workspace.php b/src/Server/Workspace.php index 26125fd..12cf3ca 100644 --- a/src/Server/Workspace.php +++ b/src/Server/Workspace.php @@ -95,7 +95,7 @@ class Workspace * * @param SymbolDescriptor $query Partial metadata about the symbol that is being searched for. * @param string[] $files An optional list of files to restrict the search to. - * @return Observable ReferenceInformation[] + * @return Observable That emits JSON Patch operations that eventually result in ReferenceInformation[] */ public function xreferences($query, array $files = null): Observable { @@ -109,25 +109,20 @@ class Workspace return observableFromEvent($this->index, 'complete')->take(1); } }) - // Get all definition FQNs in dependencies - ->flatMap(function () { + // Get all definitions in dependencies + ->flatMap(function () use ($query) { if (isset($query->fqsen)) { - $fqns = [$this->dependenciesIndex->getDefinition($query->fqsen)]; + $defs = [$this->dependenciesIndex->getDefinition($query->fqsen)]; } else { - $fqns = $this->dependenciesIndex->getDefinitions(); + $defs = $this->dependenciesIndex->getDefinitions(); } - return Observable::fromArray($fqns); + return Observable::fromArray($defs); }) - // Get all URIs in the project source that reference those definitions - ->flatMap(function (Definition $def) { - return Observable::fromArray($this->sourceIndex->getReferenceUris($fqn)); - }) - ->distinct() - ->flatMap(function (string $uri) { - return $this->documentLoader->getOrLoad($uri); + ->map(function (Definition $def) { + // Create SymbolDescriptor for Definition $symbol = new SymbolDescriptor; - $symbol->fqsen = $fqn; - foreach (get_object_vars($def->symbolInformation) as $prop => $val) { + $symbol->fqsen = $def->fqn; + foreach (get_object_vars($def->symbolInformation) as $prop => &$val) { $symbol->$prop = $val; } // Find out package name @@ -139,52 +134,72 @@ class Workspace break; } } + return $symbol; }) + ->filter(function (SymbolDescriptor $symbol) { // If there was no FQSEN provided, check if query attributes match + $matches = true; if (!isset($query->fqsen)) { - $matches = true; foreach (get_object_vars($query) as $prop => $val) { if ($query->$prop != $symbol->$prop) { $matches = false; break; } } - if (!$matches) { - continue; - } - } - foreach ($doc->getReferenceNodesByFqn($fqn) as $node) { - $refInfo = new ReferenceInformation; - $refInfo->reference = Location::fromNode($node); - $refInfo->symbol = $symbol; - $refInfos[] = $refInfo; } + return $matches; }) - ->flatMap(function (PhpDocument $doc) use ($fqn) { - + // Get all URIs in the project source that reference those definitions + ->flatMap(function (SymbolDescriptor $symbol) { + return Observable::fromArray($this->sourceIndex->getReferenceUris($symbol->fqsen))->map(function ($uri) use ($symbol) { + return ['uri' => $uri, 'symbol' => $symbol]; + }); + }) + // ['uri' => string, 'symbol' => SymbolDescriptor] + ->groupBy(function (array $ref) { + return $ref['uri']; + }) + // Observable<['uri' => string, 'symbol' => SymbolDescriptor]> + ->map(function (Observable $refs) { + // Get document by URI + $uri = $refs->getKey(); + return $this->documentLoader->getOrLoad($uri) + ->flatMap(function (PhpDocument $doc) use ($refs) { + return $refs + ->pluck('symbol') + ->flatMap(function (SymbolDescriptor $symbol) use ($doc) { + return Observable::fromArray($doc->getReferenceNodesByFqn($symbol->fqsen)) + ->map(function (Node $node) use ($symbol) { + return new ReferenceInformation(Location::fromNode($node), $symbol); + }); + }); + }); + }) + ->map(function (ReferenceInformation $refInfo) { + return new Operation\Add('/-', $refInfo); }) - $refInfos = []; - foreach ($refs as $uri => $fqns) { - foreach ($fqns as $fqn) { - } - } - return $refInfos; - }) ->startWith(new Operation\Replace('/', [])); } /** - * @return DependencyReference[] + * @return Observable for JSON Patch operations of DependencyReference[] */ - public function xdependencies(): array + public function xdependencies(): Observable { - if ($this->composerLock === null) { - return []; - } - $dependencyReferences = []; - foreach ($this->composerLock->packages as $package) { - $dependencyReferences[] = new DependencyReference($package); - } - return $dependencyReferences; + return Observable::just(null) + ->flatMap(function () { + if ($this->composerLock === null) { + return Observable::empty(); + } + $dependencyReferences = []; + return Observable::fromArray(array_merge( + $this->composerLock->packages, + $this->composerLock->{'packages-dev'} + )); + }) + ->map(function (\stdClass $package) { + return new Operation\Add('/-', new DependencyReference($package)); + }) + ->startWith(new Operation\Replace('/', [])); } }