1
0
Fork 0
observables
Felix Becker 2017-01-28 01:07:06 +01:00
parent 9c590e38da
commit 14ff2c46b0
2 changed files with 111 additions and 103 deletions

View File

@ -171,11 +171,11 @@ class TextDocument
*
* @param TextDocumentIdentifier $textDocument The document to format
* @param FormattingOptions $options The format options
* @return Promise <TextEdit[]>
* @return Observable <TextEdit[]>
*/
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 <Location[]>
* @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('/', []));
}
/**

View File

@ -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('/', []));
}
}