Skip to content

Commit b7c4068

Browse files
Handle progress messages (#36)
* gitignore: add .phpunit.result.cache * php-cs-fixer: upgrade to ^3.0 * WorkDoneProgress: create a client with a basic implementation * WorkDoneProgress: add WorkDoneToken Just a VO to represent a token and hide the detail of its generation. It will be easier to maintain in time if there is changes in the spec. * WorkDoneProgress: add Notifier interface to handle clinet capability
1 parent ac37100 commit b7c4068

13 files changed

+543
-11
lines changed

.gitignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@
77
/stubs
88
/doc/_build
99
/.vscode
10-
.php_cs.cache
10+
.php_cs.cache
11+
.phpunit.result.cache
12+
.php-cs-fixer.cache

.php_cs.dist .php-cs-fixer.dist.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
])
99
;
1010

11-
return PhpCsFixer\Config::create()
11+
return (new PhpCsFixer\Config())
1212
->setRiskyAllowed(true)
1313
->setRules([
1414
'@PSR2' => true,

composer.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"require-dev": {
2323
"amphp/phpunit-util": "^1.3",
2424
"ergebnis/composer-normalize": "^2.0",
25-
"friendsofphp/php-cs-fixer": "^2.17",
25+
"friendsofphp/php-cs-fixer": "^3.0",
2626
"jangregor/phpstan-prophecy": "^0.8.0",
2727
"phpactor/phly-event-dispatcher": "~2.0.0",
2828
"phpactor/test-utils": "~1.1.3",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?php
2+
3+
namespace Phpactor\LanguageServer\Core\Server\Client;
4+
5+
use Amp\Promise;
6+
use InvalidArgumentException;
7+
use Phpactor\LanguageServer\Core\Rpc\ResponseMessage;
8+
use Phpactor\LanguageServer\Core\Server\RpcClient;
9+
use Phpactor\LanguageServer\WorkDoneProgress\WorkDoneToken;
10+
11+
final class WorkDoneProgressClient
12+
{
13+
/**
14+
* @var RpcClient
15+
*/
16+
private $client;
17+
18+
public function __construct(RpcClient $client)
19+
{
20+
$this->client = $client;
21+
}
22+
23+
/**
24+
* @return Promise<ResponseMessage>
25+
*/
26+
public function create(WorkDoneToken $token): Promise
27+
{
28+
return \Amp\call(function () use ($token) {
29+
return yield $this->client->request('window/workDoneProgress/create', [
30+
'token' => (string) $token,
31+
]);
32+
});
33+
}
34+
35+
public function begin(
36+
WorkDoneToken $token,
37+
string $title,
38+
?string $message = null,
39+
?int $percentage = null,
40+
?bool $cancellable = null
41+
): void {
42+
self::assertIsValidPercentage($percentage);
43+
44+
$this->notify($token, [
45+
'kind' => 'begin',
46+
'title' => $title,
47+
'message' => $message,
48+
'percentage' => $percentage,
49+
'cancellable' => $cancellable,
50+
]);
51+
}
52+
53+
public function report(
54+
WorkDoneToken $token,
55+
?string $message = null,
56+
?int $percentage = null,
57+
?bool $cancellable = null
58+
): void {
59+
self::assertIsValidPercentage($percentage);
60+
61+
$this->notify($token, [
62+
'kind' => 'report',
63+
'message' => $message,
64+
'percentage' => $percentage,
65+
'cancellable' => $cancellable,
66+
]);
67+
}
68+
69+
public function end(WorkDoneToken $token, ?string $message = null): void
70+
{
71+
$this->notify($token, [
72+
'kind' => 'end',
73+
'message' => $message,
74+
]);
75+
}
76+
77+
private static function assertIsValidPercentage(?int $percentage): void
78+
{
79+
if (!(null === $percentage || 0 <= $percentage && $percentage <= 100)) {
80+
throw new InvalidArgumentException(
81+
'The percentage must be an integer comprised between 0 and 100.',
82+
);
83+
}
84+
}
85+
86+
private function notify(WorkDoneToken $token, array $value): void
87+
{
88+
assert(in_array($value['kind'], ['begin', 'report', 'end']));
89+
90+
$this->client->notification('$/progress', [
91+
'token' => (string) $token,
92+
'value' => $value,
93+
]);
94+
}
95+
}

lib/Core/Server/ClientApi.php

+6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Phpactor\LanguageServer\Core\Server\Client\ClientClient;
66
use Phpactor\LanguageServer\Core\Server\Client\DiagnosticsClient;
77
use Phpactor\LanguageServer\Core\Server\Client\WindowClient;
8+
use Phpactor\LanguageServer\Core\Server\Client\WorkDoneProgressClient;
89
use Phpactor\LanguageServer\Core\Server\Client\WorkspaceClient;
910

1011
final class ClientApi
@@ -38,4 +39,9 @@ public function diagnostics(): DiagnosticsClient
3839
{
3940
return new DiagnosticsClient($this->client);
4041
}
42+
43+
public function workDoneProgress(): WorkDoneProgressClient
44+
{
45+
return new WorkDoneProgressClient($this->client);
46+
}
4147
}

lib/Core/Server/RpcClient/TestRpcClient.php

-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
namespace Phpactor\LanguageServer\Core\Server\RpcClient;
44

55
use Amp\Promise;
6-
use Phpactor\LanguageServer\Core\Server\ResponseWatcher;
76
use Phpactor\LanguageServer\Core\Server\ResponseWatcher\TestResponseWatcher;
87
use Phpactor\LanguageServer\Core\Server\RpcClient;
98
use Phpactor\LanguageServer\Core\Server\Transmitter\TestMessageTransmitter;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
namespace Phpactor\LanguageServer\WorkDoneProgress;
4+
5+
use Amp\Promise;
6+
use Phpactor\LanguageServerProtocol\ClientCapabilities;
7+
use Phpactor\LanguageServer\Core\Server\ClientApi;
8+
9+
final class ClientCapabilityDependentProgressNotifier implements ProgressNotifier
10+
{
11+
/**
12+
* @var ProgressNotifier
13+
*/
14+
private $notifier;
15+
16+
public function __construct(ClientApi $api, ClientCapabilities $capabilities)
17+
{
18+
if ($capabilities->window['workDoneProgress'] ?? false) {
19+
$this->notifier = new WorkDoneProgressNotifier($api);
20+
} else {
21+
$this->notifier = new MessageProgressNotifier($api);
22+
}
23+
}
24+
25+
/**
26+
* {@inheritDoc}
27+
*/
28+
public function create(WorkDoneToken $token): Promise
29+
{
30+
return $this->notifier->create($token);
31+
}
32+
33+
/**
34+
* {@inheritDoc}
35+
*/
36+
public function begin(
37+
WorkDoneToken $token,
38+
string $title,
39+
?string $message = null,
40+
?int $percentage = null,
41+
?bool $cancellable = null
42+
): void {
43+
$this->notifier->begin($token, $title, $message, $percentage, $cancellable);
44+
}
45+
46+
/**
47+
* {@inheritDoc}
48+
*/
49+
public function report(
50+
WorkDoneToken $token,
51+
?string $message = null,
52+
?int $percentage = null,
53+
?bool $cancellable = null
54+
): void {
55+
$this->notifier->report($token, $message, $percentage, $cancellable);
56+
}
57+
58+
public function end(WorkDoneToken $token, ?string $message = null): void
59+
{
60+
$this->notifier->end($token, $message);
61+
}
62+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
namespace Phpactor\LanguageServer\WorkDoneProgress;
4+
5+
use Amp\Promise;
6+
use Amp\Success;
7+
use Phpactor\LanguageServer\Core\Rpc\ResponseMessage;
8+
use Phpactor\LanguageServer\Core\Server\ClientApi;
9+
use Phpactor\LanguageServer\Core\Server\Client\MessageClient;
10+
use Ramsey\Uuid\Uuid;
11+
12+
final class MessageProgressNotifier implements ProgressNotifier
13+
{
14+
/**
15+
* @var MessageClient
16+
*/
17+
private $api;
18+
19+
public function __construct(ClientApi $api)
20+
{
21+
$this->api = $api->window()->showMessage();
22+
}
23+
24+
/**
25+
* {@inheritDoc}
26+
*/
27+
public function create(WorkDoneToken $token): Promise
28+
{
29+
return new Success(new ResponseMessage(
30+
Uuid::uuid4(),
31+
null,
32+
));
33+
}
34+
35+
/**
36+
* {@inheritDoc}
37+
*/
38+
public function begin(
39+
WorkDoneToken $token,
40+
string $title,
41+
?string $message = null,
42+
?int $percentage = null,
43+
?bool $cancellable = null
44+
): void {
45+
$this->api->info($message);
46+
}
47+
48+
/**
49+
* {@inheritDoc}
50+
*/
51+
public function report(WorkDoneToken $token, ?string $message = null, ?int $percentage = null, ?bool $cancellable = null): void
52+
{
53+
$this->api->info(sprintf('%s - %d%%', $message, $percentage));
54+
}
55+
56+
public function end(WorkDoneToken $token, ?string $message = null): void
57+
{
58+
$this->api->info($message);
59+
}
60+
}
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
namespace Phpactor\LanguageServer\WorkDoneProgress;
4+
5+
use Amp\Promise;
6+
use Phpactor\LanguageServer\Core\Rpc\ResponseMessage;
7+
8+
interface ProgressNotifier
9+
{
10+
/**
11+
* @return Promise<ResponseMessage>
12+
*/
13+
public function create(WorkDoneToken $token): Promise;
14+
15+
/**
16+
* @param int|null $percentage Percentage comprised between 0 and 100
17+
*/
18+
public function begin(
19+
WorkDoneToken $token,
20+
string $title,
21+
?string $message = null,
22+
?int $percentage = null,
23+
?bool $cancellable = null
24+
): void;
25+
26+
/**
27+
* @param int|null $percentage Percentage comprised between 0 and 100
28+
*/
29+
public function report(
30+
WorkDoneToken $token,
31+
?string $message = null,
32+
?int $percentage = null,
33+
?bool $cancellable = null
34+
): void;
35+
36+
public function end(WorkDoneToken $token, ?string $message = null): void;
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
namespace Phpactor\LanguageServer\WorkDoneProgress;
4+
5+
use Amp\Promise;
6+
use Phpactor\LanguageServer\Core\Server\ClientApi;
7+
use Phpactor\LanguageServer\Core\Server\Client\WorkDoneProgressClient;
8+
9+
final class WorkDoneProgressNotifier implements ProgressNotifier
10+
{
11+
/**
12+
* @var WorkDoneProgressClient
13+
*/
14+
private $api;
15+
16+
public function __construct(ClientApi $api)
17+
{
18+
$this->api = $api->workDoneProgress();
19+
}
20+
21+
/**
22+
* {@inheritDoc}
23+
*/
24+
public function create(WorkDoneToken $token): Promise
25+
{
26+
return $this->api->create($token);
27+
}
28+
29+
/**
30+
* {@inheritDoc}
31+
*/
32+
public function begin(
33+
WorkDoneToken $token,
34+
string $title,
35+
?string $message = null,
36+
?int $percentage = null,
37+
?bool $cancellable = null
38+
): void {
39+
$this->api->begin($token, $title, $message, $percentage, $cancellable);
40+
}
41+
42+
/**
43+
* {@inheritDoc}
44+
*/
45+
public function report(
46+
WorkDoneToken $token,
47+
?string $message = null,
48+
?int $percentage = null,
49+
?bool $cancellable = null
50+
): void {
51+
$this->api->report($token, $message, $percentage, $cancellable);
52+
}
53+
54+
public function end(WorkDoneToken $token, ?string $message = null): void
55+
{
56+
$this->api->end($token, $message);
57+
}
58+
}
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace Phpactor\LanguageServer\WorkDoneProgress;
4+
5+
use Ramsey\Uuid\Uuid;
6+
7+
final class WorkDoneToken
8+
{
9+
/**
10+
* @var string
11+
*/
12+
private $token;
13+
14+
public function __construct(string $token)
15+
{
16+
$this->token = $token;
17+
}
18+
19+
public function __toString(): string
20+
{
21+
return $this->token;
22+
}
23+
24+
public static function generate(): self
25+
{
26+
return new self((string) Uuid::uuid4());
27+
}
28+
}

0 commit comments

Comments
 (0)