|
8 | 8 | use Symfony\Lsp\Document\DocumentContextResolver; |
9 | 9 | use Symfony\Lsp\Document\DocumentStore; |
10 | 10 | use Symfony\Lsp\Document\PositionConverter; |
| 11 | +use Symfony\Lsp\Document\Range; |
11 | 12 | use Symfony\Lsp\Feature\Environment\EnvironmentExtractor; |
12 | 13 | use Symfony\Lsp\Feature\Environment\EnvironmentIndexRegistry; |
13 | 14 | use Symfony\Lsp\Feature\Environment\EnvironmentProvider; |
@@ -36,6 +37,76 @@ public function testIndexesNamesAndReferencesWithoutValues(): void |
36 | 37 | self::assertSame(['REAL_ENV'], array_map(static fn ($item): string => $item->name, $twigFacts->references)); |
37 | 38 | } |
38 | 39 |
|
| 40 | + #[DataProvider('yamlScalarContextProvider')] |
| 41 | + public function testSupportsEnvironmentExpressionsInYamlScalarContexts(string $text): void |
| 42 | + { |
| 43 | + $uri = 'file:///workspace/config/services.yaml'; |
| 44 | + $documents = new DocumentStore(); |
| 45 | + $documents->open(new Document($uri, 'yaml', 1, $text)); |
| 46 | + $projects = new ProjectRegistry(); |
| 47 | + $projects->replace([$project = new Project('/workspace', 'file:///workspace', '^8.0')]); |
| 48 | + $converter = new PositionConverter(); |
| 49 | + $twigComments = new TwigCommentParser(); |
| 50 | + $phpComments = new PhpCommentParser(); |
| 51 | + $yamlComments = new YamlCommentParser(); |
| 52 | + $xmlComments = new XmlCommentParser(); |
| 53 | + $extractor = new EnvironmentExtractor($converter, new UriToPathConverter(), $twigComments, $phpComments, $yamlComments, $xmlComments); |
| 54 | + $indexes = new EnvironmentIndexRegistry(); |
| 55 | + $indexes->forProject($project)->replaceSources( |
| 56 | + $extractor->extract('file:///workspace/.env', 'dotenv', "PARTIAL_ENV=value\n"), |
| 57 | + $extractor->extract($uri, 'yaml', $text), |
| 58 | + ); |
| 59 | + $provider = new EnvironmentProvider(new DocumentContextResolver($documents, $projects), $converter, new LspProtocolMapper(), $indexes, $extractor, $twigComments, $phpComments, $yamlComments, $xmlComments); |
| 60 | + |
| 61 | + $facts = $extractor->extract($uri, 'yaml', $text); |
| 62 | + self::assertSame(['COMPLETE_ENV'], array_map(static fn ($reference): string => $reference->name, $facts->references)); |
| 63 | + self::assertSame($this->protocolRange($converter, $text, (int) strpos($text, 'COMPLETE_ENV'), \strlen('COMPLETE_ENV')), $this->protocolRangeFromObject($facts->references[0]->range)); |
| 64 | + |
| 65 | + $completionStart = (int) strpos($text, 'PARTIAL_EN'); |
| 66 | + $completionOffset = $completionStart + \strlen('PARTIAL_EN'); |
| 67 | + $completion = $provider->complete($this->positionParams($converter, $uri, $text, $completionOffset)) ?? []; |
| 68 | + self::assertSame(['PARTIAL_ENV'], array_column($completion, 'label')); |
| 69 | + /** @var array{range: array{start: array{line: int, character: int}, end: array{line: int, character: int}}} $textEdit */ |
| 70 | + $textEdit = $completion[0]['textEdit']; |
| 71 | + self::assertSame($this->protocolRange($converter, $text, $completionStart, \strlen('PARTIAL_EN')), $textEdit['range']); |
| 72 | + |
| 73 | + $malformed = '%env(MALFORMED_ENV%'; |
| 74 | + $malformedOffset = (int) strpos($text, $malformed); |
| 75 | + $diagnostics = $provider->diagnostics(['textDocument' => ['uri' => $uri]]) ?? []; |
| 76 | + self::assertSame(['env.malformed_chain'], array_column($diagnostics, 'code')); |
| 77 | + self::assertSame($this->protocolRange($converter, $text, $malformedOffset, \strlen($malformed)), $diagnostics[0]['range'] ?? null); |
| 78 | + } |
| 79 | + |
| 80 | + /** @return iterable<string, array{string}> */ |
| 81 | + public static function yamlScalarContextProvider(): iterable |
| 82 | + { |
| 83 | + yield 'double quoted with escapes' => [<<<'YAML' |
| 84 | + complete: "escaped\\n%env(COMPLETE_ENV)%" |
| 85 | + completion: "escaped\\t%env(PARTIAL_EN" |
| 86 | + malformed: "escaped\\u0020%env(MALFORMED_ENV%" |
| 87 | + YAML]; |
| 88 | + yield 'block scalar' => [<<<'YAML' |
| 89 | + complete: |- |
| 90 | + %env(COMPLETE_ENV)% |
| 91 | + completion: |- |
| 92 | + %env(PARTIAL_EN |
| 93 | + malformed: |- |
| 94 | + %env(MALFORMED_ENV% |
| 95 | + YAML]; |
| 96 | + yield 'block sequence item' => [<<<'YAML' |
| 97 | + values: |
| 98 | + - '%env(COMPLETE_ENV)%' |
| 99 | + - '%env(PARTIAL_EN' |
| 100 | + - '%env(MALFORMED_ENV%' |
| 101 | + YAML]; |
| 102 | + yield 'environment section' => [<<<'YAML' |
| 103 | + when@test: |
| 104 | + complete: '%env(COMPLETE_ENV)%' |
| 105 | + completion: '%env(PARTIAL_EN' |
| 106 | + malformed: '%env(MALFORMED_ENV%' |
| 107 | + YAML]; |
| 108 | + } |
| 109 | +
|
39 | 110 | public function testCompletesHoversNavigatesAndDiagnosesProcessors(): void |
40 | 111 | { |
41 | 112 | $uri = 'file:///workspace/config/services.yaml'; |
@@ -200,6 +271,15 @@ private function positionParams(PositionConverter $converter, string $uri, strin |
200 | 271 | return ['textDocument' => ['uri' => $uri], 'position' => ['line' => $position->line, 'character' => $position->character]]; |
201 | 272 | } |
202 | 273 |
|
| 274 | + /** @return array{start: array{line: int, character: int}, end: array{line: int, character: int}} */ |
| 275 | + private function protocolRangeFromObject(Range $range): array |
| 276 | + { |
| 277 | + return [ |
| 278 | + 'start' => ['line' => $range->start->line, 'character' => $range->start->character], |
| 279 | + 'end' => ['line' => $range->end->line, 'character' => $range->end->character], |
| 280 | + ]; |
| 281 | + } |
| 282 | +
|
203 | 283 | /** @return array{start: array{line: int, character: int}, end: array{line: int, character: int}} */ |
204 | 284 | private function protocolRange(PositionConverter $converter, string $text, int $offset, int $length): array |
205 | 285 | { |
|
0 commit comments