forked from phpactor/language-server
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathHandlerMethodRunner.php
More file actions
99 lines (84 loc) · 3.27 KB
/
HandlerMethodRunner.php
File metadata and controls
99 lines (84 loc) · 3.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
<?php
namespace Phpactor\LanguageServer\Core\Handler;
use Amp\CancellationTokenSource;
use Amp\Promise;
use Amp\Success;
use Phpactor\LanguageServer\Core\Dispatcher\ArgumentResolver;
use Phpactor\LanguageServer\Core\Dispatcher\ArgumentResolver\PassThroughArgumentResolver;
use Phpactor\LanguageServer\Core\Rpc\Message;
use Phpactor\LanguageServer\Core\Rpc\NotificationMessage;
use Phpactor\LanguageServer\Core\Rpc\RequestMessage;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use RuntimeException;
use Phpactor\LanguageServer\Core\Rpc\ResponseMessage;
final class HandlerMethodRunner implements MethodRunner
{
/**
* @var array<string|int, CancellationTokenSource>
*/
private $cancellations = [];
public function __construct(
private Handlers $handlers,
private ArgumentResolver $argumentResolver = new PassThroughArgumentResolver(),
private LoggerInterface $logger = new NullLogger(),
private HandlerMethodResolver $resolver = new HandlerMethodResolver(),
) {
}
/**
* @return Promise<ResponseMessage|null>
*/
public function dispatch(Message $request): Promise
{
if (
!$request instanceof NotificationMessage &&
!$request instanceof RequestMessage
) {
throw new RuntimeException(sprintf(
'Message must either be a Notification or a Request, got "%s"',
get_class($request)
));
}
return \Amp\call(function () use ($request) {
$handler = $this->handlers->get($request->method);
$method = $this->resolver->resolveHandlerMethod($handler, $request->method);
$cancellationTokenSource = new CancellationTokenSource();
// we only cancel requests (that have IDs) and not notifications
if ($request instanceof RequestMessage) {
$this->cancellations[$request->id] = $cancellationTokenSource;
}
$args = array_values($this->argumentResolver->resolveArguments($handler, $method, $request));
$args[] = $cancellationTokenSource->getToken();
$promise = $handler->$method(...$args) ?? new Success(null);
if (!$promise instanceof Promise) {
throw new RuntimeException(sprintf(
'Handler "%s:%s" must return instance of Amp\\Promise, got "%s"',
get_class($handler),
$method,
is_object($promise) ? get_class($promise) : gettype($promise)
));
}
if (!$request instanceof RequestMessage) {
return null;
}
return new ResponseMessage($request->id, yield $promise);
});
}
/**
* @param string|int $id
*/
public function cancelRequest($id): void
{
if (!isset($this->cancellations[$id])) {
$this->logger->warning(sprintf(
'Trying to cancel non-running request "%s", running requests: "%s"',
$id,
implode('", "', array_keys($this->cancellations))
));
return;
}
$tokenSource = $this->cancellations[$id];
assert($tokenSource instanceof CancellationTokenSource);
$tokenSource->cancel();
}
}