WIP
parent
47b5b6709c
commit
f43c41739e
|
@ -37,7 +37,8 @@
|
||||||
"webmozart/glob": "^4.1",
|
"webmozart/glob": "^4.1",
|
||||||
"sabre/uri": "^2.0",
|
"sabre/uri": "^2.0",
|
||||||
"JetBrains/phpstorm-stubs": "dev-master",
|
"JetBrains/phpstorm-stubs": "dev-master",
|
||||||
"composer/composer": "^1.3"
|
"composer/composer": "^1.3",
|
||||||
|
"reactivex/rxphp": "^1.5"
|
||||||
},
|
},
|
||||||
"repositories": [
|
"repositories": [
|
||||||
{
|
{
|
||||||
|
|
|
@ -122,40 +122,66 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher
|
||||||
$this->shutdown();
|
$this->shutdown();
|
||||||
$this->exit();
|
$this->exit();
|
||||||
});
|
});
|
||||||
$this->protocolReader->on('message', function (Message $msg) {
|
// Map from request ID to subscription
|
||||||
coroutine(function () use ($msg) {
|
$subscriptions = [];
|
||||||
|
$this->protocolReader->on('message', function (Message $msg) use ($subscriptions) {
|
||||||
// Ignore responses, this is the handler for requests and notifications
|
// Ignore responses, this is the handler for requests and notifications
|
||||||
if (AdvancedJsonRpc\Response::isResponse($msg->body)) {
|
if (AdvancedJsonRpc\Response::isResponse($msg->body)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if ($msg->body->method === '$/cancelRequest') {
|
||||||
|
if (!isset($subscriptions[$msg->body->id])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Express that we are not interested anymore in the observable
|
||||||
|
$subscriptions[$msg->body->id]->dispose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// The result object that is built through JSON patches
|
||||||
$result = null;
|
$result = null;
|
||||||
$error = null;
|
$error = null;
|
||||||
try {
|
try {
|
||||||
// Invoke the method handler to get a result
|
// Invoke the method handler to get a result
|
||||||
$result = yield $this->dispatch($msg->body);
|
$obs = $this->dispatch($msg->body);
|
||||||
} catch (AdvancedJsonRpc\Error $e) {
|
} catch (\Throwable $e) {
|
||||||
// If a ResponseError is thrown, send it back in the Response
|
$obs = Observable::error($e);
|
||||||
$error = $e;
|
}
|
||||||
} catch (Throwable $e) {
|
// Notifications dont need further acting
|
||||||
// If an unexpected error occured, send back an INTERNAL_ERROR error response
|
if (AdvancedJsonRpc\Notification::isNotification($msg->body)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!($obs instanceof ObservableInterface)) {
|
||||||
|
$obs = Observable::just($obs);
|
||||||
|
}
|
||||||
|
$subscriptions[$msg->body->id] = $obs->subscribe(new CallbackObserver(
|
||||||
|
function (JSONPatch $patch) use (&$result) {
|
||||||
|
$this->protocolWriter->write(
|
||||||
|
new Message(
|
||||||
|
new AdvancedJsonRpc\Notification(
|
||||||
|
'$/partialResult', ['id' => $msg->body->id, 'patch' => $patch]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
// Apply path to result object for BC
|
||||||
|
$patch->apply($result);
|
||||||
|
},
|
||||||
|
function (\Exception $error) use ($msg) {
|
||||||
|
if (!($error instanceof AdvancedJsonRpc\Error)) {
|
||||||
$error = new AdvancedJsonRpc\Error(
|
$error = new AdvancedJsonRpc\Error(
|
||||||
(string)$e,
|
(string)$error,
|
||||||
AdvancedJsonRpc\ErrorCode::INTERNAL_ERROR,
|
AdvancedJsonRpc\ErrorCode::INTERNAL_ERROR,
|
||||||
null,
|
null,
|
||||||
$e
|
$error
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Only send a Response for a Request
|
// If an unexpected error occured, send back an INTERNAL_ERROR error response
|
||||||
// Notifications do not send Responses
|
$this->protocolWriter->write(new Message(new AdvancedJsonRpc\ErrorResponse($msg->body->id, $error)));
|
||||||
if (AdvancedJsonRpc\Request::isRequest($msg->body)) {
|
},
|
||||||
if ($error !== null) {
|
function () use ($msg, &$result) {
|
||||||
$responseBody = new AdvancedJsonRpc\ErrorResponse($msg->body->id, $error);
|
// Return the built result object for BC
|
||||||
} else {
|
$this->protocolWriter->write(new Message(new AdvancedJsonRpc\SuccessResponse($msg->body->id, $result)));
|
||||||
$responseBody = new AdvancedJsonRpc\SuccessResponse($msg->body->id, $result);
|
|
||||||
}
|
}
|
||||||
$this->protocolWriter->write(new Message($responseBody));
|
));
|
||||||
}
|
|
||||||
})->otherwise('\\LanguageServer\\crash');
|
|
||||||
});
|
});
|
||||||
$this->protocolWriter = $writer;
|
$this->protocolWriter = $writer;
|
||||||
$this->client = new LanguageClient($reader, $writer);
|
$this->client = new LanguageClient($reader, $writer);
|
||||||
|
|
|
@ -62,10 +62,31 @@ class Workspace
|
||||||
* The workspace symbol request is sent from the client to the server to list project-wide symbols matching the query string.
|
* The workspace symbol request is sent from the client to the server to list project-wide symbols matching the query string.
|
||||||
*
|
*
|
||||||
* @param string $query
|
* @param string $query
|
||||||
* @return Promise <SymbolInformation[]>
|
* @return Observable <SymbolInformation[]>
|
||||||
*/
|
*/
|
||||||
public function symbol(string $query): Promise
|
public function symbol(string $query): Observable
|
||||||
{
|
{
|
||||||
|
(
|
||||||
|
$this->index->isStaticComplete()
|
||||||
|
? observableFromEvent($this->index->once('static-complete', function () {
|
||||||
|
|
||||||
|
})
|
||||||
|
: Observable::empty()
|
||||||
|
)
|
||||||
|
return Observable::create(function (ObserverInterface $observer) {
|
||||||
|
// Wait until indexing for definitions finished
|
||||||
|
if (!$this->index->isStaticComplete()) {
|
||||||
|
$this->index->once('static-complete', function () {
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
$observer->onNext(42);
|
||||||
|
$observer->onCompleted();
|
||||||
|
|
||||||
|
return new CallbackDisposable(function () {
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
return coroutine(function () use ($query) {
|
return coroutine(function () use ($query) {
|
||||||
// Wait until indexing for definitions finished
|
// Wait until indexing for definitions finished
|
||||||
if (!$this->index->isStaticComplete()) {
|
if (!$this->index->isStaticComplete()) {
|
||||||
|
|
|
@ -93,6 +93,20 @@ function waitForEvent(EmitterInterface $emitter, string $event): Promise
|
||||||
return $p;
|
return $p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a promise that is fulfilled once the passed event was triggered on the passed EventEmitter
|
||||||
|
*
|
||||||
|
* @param EmitterInterface $emitter
|
||||||
|
* @param string $event
|
||||||
|
* @return Promise
|
||||||
|
*/
|
||||||
|
function observableFromEvent(EmitterInterface $emitter, string $event): Promise
|
||||||
|
{
|
||||||
|
Observable::create()
|
||||||
|
$emitter->once($event, [$p, 'fulfill']);
|
||||||
|
return $p;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the closest node of a specific type
|
* Returns the closest node of a specific type
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue