diff --git a/src/Psalm/Internal/LanguageServer/Client/Progress/LegacyProgress.php b/src/Psalm/Internal/LanguageServer/Client/Progress/LegacyProgress.php new file mode 100644 index 00000000000..ca021d7cd63 --- /dev/null +++ b/src/Psalm/Internal/LanguageServer/Client/Progress/LegacyProgress.php @@ -0,0 +1,59 @@ +handler = $handler; + } + + public function begin(string $title, ?string $message = null, ?int $percentage): void + { + if ($this->title !== null) { + throw new LogicException('Progress has already been started'); + } + + $this->title = $title; + + $this->notify($message); + } + + public function update(?string $message = null, ?int $percentage): void + { + if ($this->title === null) { + throw new LogicException('The progress has not been started yet'); + } + + $this->notify($message); + } + + public function end(?string $message): void + { + if ($this->title === null) { + throw new LogicException('The progress has not been started yet'); + } + + $this->notify($message); + } + + private function notify(?string $message): void + { + $this->handler->notify( + 'telemetry/event', + new LogMessage( + $this->title . (empty($message) ? '' : (': ' . $message)), + MessageType::INFO, + ), + ); + } +} diff --git a/src/Psalm/Internal/LanguageServer/Client/Progress/Progress.php b/src/Psalm/Internal/LanguageServer/Client/Progress/Progress.php new file mode 100644 index 00000000000..e30aefc5146 --- /dev/null +++ b/src/Psalm/Internal/LanguageServer/Client/Progress/Progress.php @@ -0,0 +1,101 @@ +handler = $handler; + $this->token = $token; + } + + public function begin( + string $title, + ?string $message = null, + ?int $percent = null + ): void { + if ($this->finished) { + throw new LogicException('Progress has already been finished'); + } + + $notification = [ + 'token' => $this->token, + 'value' => [ + 'kind' => 'begin', + 'title' => $title, + ], + ]; + if ($message !== null) { + $notification['value']['message'] = $message; + } + + if ($percent !== null) { + $notification['value']['percentage'] = $percent; + $this->withPercentage = true; + } + + $this->handler->notify('$/progress', $notification); + } + + public function end(?string $message = null): void + { + if ($this->finished) { + throw new LogicException('Progress has already been finished'); + } + + $notification = [ + 'token' => $this->token, + 'value' => [ + 'kind' => 'end', + ], + ]; + + if ($message !== null) { + $notification['value']['message'] = $message; + } + + $this->handler->notify('$/progress', $notification); + + $this->finished = true; + } + + public function update(?string $message = null, ?int $percentage = null): void + { + if ($this->finished) { + throw new LogicException('Progress has already been finished'); + } + + $notification = [ + 'token' => $this->token, + 'value' => [ + 'kind' => 'report', + ], + ]; + + if ($message !== null) { + $notification['value']['message'] = $message; + } + + if ($percentage !== null) { + if ($this->withPercentage) { + throw new LogicException( + 'Cannot update percentage for progress ' + . 'that was started with percentage', + ); + } + $notification['value']['percentage'] = $percentage; + } + + $this->handler->notify('$/progress', $notification); + } +} diff --git a/src/Psalm/Internal/LanguageServer/Client/Progress/ProgressInterface.php b/src/Psalm/Internal/LanguageServer/Client/Progress/ProgressInterface.php new file mode 100644 index 00000000000..4c9058907e6 --- /dev/null +++ b/src/Psalm/Internal/LanguageServer/Client/Progress/ProgressInterface.php @@ -0,0 +1,15 @@ +server->clientCapabilities->window->workDoneProgress ?? false) { + return new Progress($this->handler, $token); + } else { + return new LegacyProgress($this->handler); + } + } + /** * Configuration Refreshed from Client * diff --git a/src/Psalm/Internal/LanguageServer/LanguageServer.php b/src/Psalm/Internal/LanguageServer/LanguageServer.php index 3fa4261c8d3..5a2d6482175 100644 --- a/src/Psalm/Internal/LanguageServer/LanguageServer.php +++ b/src/Psalm/Internal/LanguageServer/LanguageServer.php @@ -88,6 +88,7 @@ use function strpos; use function substr; use function trim; +use function uniqid; use function urldecode; use const JSON_PRETTY_PRINT; @@ -398,7 +399,8 @@ public function initialize( ?string $rootPath = null, ?string $rootUri = null, $initializationOptions = null, - ?string $trace = null + ?string $trace = null, + ?string $workdDoneToken = null //?array $workspaceFolders = null //error in json-dispatcher ): Promise { $this->clientInfo = $clientInfo; @@ -412,9 +414,11 @@ public function initialize( return call( /** @return Generator */ - function () { + function () use ($workdDoneToken) { + $progress = $this->client->makeProgress($workdDoneToken ?? uniqid('tkn')); + $this->logInfo("Initializing..."); - $this->clientStatus('initializing'); + $progress->begin('Initialization', 'Starting'); // Eventually, this might block on something. Leave it as a generator. /** @psalm-suppress TypeDoesNotContainType */ @@ -425,14 +429,14 @@ function () { $this->project_analyzer->serverMode($this); $this->logInfo("Initializing: Getting code base..."); - $this->clientStatus('initializing', 'getting code base'); + $progress->update('Getting code base'); $this->logInfo("Initializing: Scanning files ({$this->project_analyzer->threads} Threads)..."); - $this->clientStatus('initializing', 'scanning files'); + $progress->update('Scanning files'); $this->codebase->scanFiles($this->project_analyzer->threads); $this->logInfo("Initializing: Registering stub files..."); - $this->clientStatus('initializing', 'registering stub files'); + $progress->update('Registering stub files'); $this->codebase->config->visitStubFiles($this->codebase, $this->project_analyzer->progress); if ($this->textDocument === null) { @@ -572,7 +576,7 @@ function () { } $this->logInfo("Initializing: Complete."); - $this->clientStatus('initialized'); + $progress->end('Initialized'); /** * Information about the server.