WIP
parent
9c590e38da
commit
14ff2c46b0
|
@ -171,11 +171,11 @@ class TextDocument
|
||||||
*
|
*
|
||||||
* @param TextDocumentIdentifier $textDocument The document to format
|
* @param TextDocumentIdentifier $textDocument The document to format
|
||||||
* @param FormattingOptions $options The format options
|
* @param FormattingOptions $options The format options
|
||||||
* @return Promise <TextEdit[]>
|
* @return Observable <TextEdit[]>
|
||||||
*/
|
*/
|
||||||
public function formatting(TextDocumentIdentifier $textDocument, FormattingOptions $options)
|
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();
|
return $document->getFormattedText();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -185,73 +185,66 @@ class TextDocument
|
||||||
* denoted by the given text document position.
|
* denoted by the given text document position.
|
||||||
*
|
*
|
||||||
* @param ReferenceContext $context
|
* @param ReferenceContext $context
|
||||||
* @return Promise <Location[]>
|
* @return Observable Emits JSON Patch operations that eventually result in Location[]
|
||||||
*/
|
*/
|
||||||
public function references(
|
public function references(
|
||||||
ReferenceContext $context,
|
ReferenceContext $context,
|
||||||
TextDocumentIdentifier $textDocument,
|
TextDocumentIdentifier $textDocument,
|
||||||
Position $position
|
Position $position
|
||||||
): Promise {
|
): Observable {
|
||||||
return coroutine(function () use ($textDocument, $position) {
|
return $this->documentLoader->getOrLoad($textDocument->uri)
|
||||||
$document = yield $this->documentLoader->getOrLoad($textDocument->uri);
|
->flatMap(function (PhpDocument $document) {
|
||||||
$node = $document->getNodeAtPosition($position);
|
$node = $document->getNodeAtPosition($position);
|
||||||
if ($node === null) {
|
if ($node === null) {
|
||||||
return [];
|
return Observable::empty();
|
||||||
}
|
|
||||||
$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;
|
|
||||||
}
|
}
|
||||||
// Find function/method/closure scope
|
// Variables always stay in the boundary of the file and need to be searched inside their function scope
|
||||||
$n = $node;
|
// by traversing the AST
|
||||||
while (isset($n) && !($n instanceof Node\FunctionLike)) {
|
if (
|
||||||
$n = $n->getAttribute('parentNode');
|
$node instanceof Node\Expr\Variable
|
||||||
}
|
|| $node instanceof Node\Param
|
||||||
if (!isset($n)) {
|
|| $node instanceof Node\Expr\ClosureUse
|
||||||
$n = $node->getAttribute('ownerDocument');
|
) {
|
||||||
}
|
if ($node->name instanceof Node\Expr) {
|
||||||
$traverser = new NodeTraverser;
|
return Observable::empty();
|
||||||
$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 [];
|
|
||||||
}
|
}
|
||||||
}
|
// Find function/method/closure scope
|
||||||
$refDocuments = yield Promise\all(array_map(
|
$n = $node;
|
||||||
[$this->documentLoader, 'getOrLoad'],
|
while (isset($n) && !($n instanceof Node\FunctionLike)) {
|
||||||
$this->index->getReferenceUris($fqn)
|
$n = $n->getAttribute('parentNode');
|
||||||
));
|
}
|
||||||
foreach ($refDocuments as $document) {
|
if (!isset($n)) {
|
||||||
$refs = $document->getReferenceNodesByFqn($fqn);
|
$n = $node->getAttribute('ownerDocument');
|
||||||
if ($refs !== null) {
|
}
|
||||||
foreach ($refs as $ref) {
|
$traverser = new NodeTraverser;
|
||||||
$locations[] = Location::fromNode($ref);
|
$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('/', []));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -95,7 +95,7 @@ class Workspace
|
||||||
*
|
*
|
||||||
* @param SymbolDescriptor $query Partial metadata about the symbol that is being searched for.
|
* @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.
|
* @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
|
public function xreferences($query, array $files = null): Observable
|
||||||
{
|
{
|
||||||
|
@ -109,25 +109,20 @@ class Workspace
|
||||||
return observableFromEvent($this->index, 'complete')->take(1);
|
return observableFromEvent($this->index, 'complete')->take(1);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// Get all definition FQNs in dependencies
|
// Get all definitions in dependencies
|
||||||
->flatMap(function () {
|
->flatMap(function () use ($query) {
|
||||||
if (isset($query->fqsen)) {
|
if (isset($query->fqsen)) {
|
||||||
$fqns = [$this->dependenciesIndex->getDefinition($query->fqsen)];
|
$defs = [$this->dependenciesIndex->getDefinition($query->fqsen)];
|
||||||
} else {
|
} 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
|
->map(function (Definition $def) {
|
||||||
->flatMap(function (Definition $def) {
|
// Create SymbolDescriptor for Definition
|
||||||
return Observable::fromArray($this->sourceIndex->getReferenceUris($fqn));
|
|
||||||
})
|
|
||||||
->distinct()
|
|
||||||
->flatMap(function (string $uri) {
|
|
||||||
return $this->documentLoader->getOrLoad($uri);
|
|
||||||
$symbol = new SymbolDescriptor;
|
$symbol = new SymbolDescriptor;
|
||||||
$symbol->fqsen = $fqn;
|
$symbol->fqsen = $def->fqn;
|
||||||
foreach (get_object_vars($def->symbolInformation) as $prop => $val) {
|
foreach (get_object_vars($def->symbolInformation) as $prop => &$val) {
|
||||||
$symbol->$prop = $val;
|
$symbol->$prop = $val;
|
||||||
}
|
}
|
||||||
// Find out package name
|
// Find out package name
|
||||||
|
@ -139,52 +134,72 @@ class Workspace
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return $symbol;
|
||||||
})
|
})
|
||||||
|
->filter(function (SymbolDescriptor $symbol) {
|
||||||
// If there was no FQSEN provided, check if query attributes match
|
// If there was no FQSEN provided, check if query attributes match
|
||||||
|
$matches = true;
|
||||||
if (!isset($query->fqsen)) {
|
if (!isset($query->fqsen)) {
|
||||||
$matches = true;
|
|
||||||
foreach (get_object_vars($query) as $prop => $val) {
|
foreach (get_object_vars($query) as $prop => $val) {
|
||||||
if ($query->$prop != $symbol->$prop) {
|
if ($query->$prop != $symbol->$prop) {
|
||||||
$matches = false;
|
$matches = false;
|
||||||
break;
|
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('/', []));
|
->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 Observable::just(null)
|
||||||
return [];
|
->flatMap(function () {
|
||||||
}
|
if ($this->composerLock === null) {
|
||||||
$dependencyReferences = [];
|
return Observable::empty();
|
||||||
foreach ($this->composerLock->packages as $package) {
|
}
|
||||||
$dependencyReferences[] = new DependencyReference($package);
|
$dependencyReferences = [];
|
||||||
}
|
return Observable::fromArray(array_merge(
|
||||||
return $dependencyReferences;
|
$this->composerLock->packages,
|
||||||
|
$this->composerLock->{'packages-dev'}
|
||||||
|
));
|
||||||
|
})
|
||||||
|
->map(function (\stdClass $package) {
|
||||||
|
return new Operation\Add('/-', new DependencyReference($package));
|
||||||
|
})
|
||||||
|
->startWith(new Operation\Replace('/', []));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue