Compare commits
7 Commits
b86cf0117d
...
29f46c6606
| Author | SHA1 | Date | |
|---|---|---|---|
| 29f46c6606 | |||
| 30e3e4145a | |||
| 4233b90b82 | |||
| da00e9c50c | |||
| fee89d7522 | |||
| bc95b33bd9 | |||
| 6cd24bf14f |
@ -1,4 +1,4 @@
|
|||||||
{"type":"meta","next_id":{"DSC":66,"AGD":69,"DEC":41,"PLN":107,"LSN":56,"CLSN":1}}
|
{"type":"meta","next_id":{"DSC":66,"AGD":69,"DEC":42,"PLN":111,"LSN":57,"CLSN":1}}
|
||||||
{"type":"discussion","id":"DSC-0065","status":"open","ticket":"multi-frontend-avoid-premature-abstractions","title":"Evitar abstracoes prematuras na preparacao multi-frontend","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","studio","frontend","architecture","multi-frontend","simplicity"],"agendas":[{"id":"AGD-0068","file":"AGD-0068-multi-frontend-avoid-premature-abstractions.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
{"type":"discussion","id":"DSC-0065","status":"open","ticket":"multi-frontend-avoid-premature-abstractions","title":"Evitar abstracoes prematuras na preparacao multi-frontend","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","studio","frontend","architecture","multi-frontend","simplicity"],"agendas":[{"id":"AGD-0068","file":"AGD-0068-multi-frontend-avoid-premature-abstractions.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
||||||
{"type":"discussion","id":"DSC-0064","status":"open","ticket":"multi-frontend-architectural-tests","title":"Testes arquiteturais para fronteiras multi-frontend","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","studio","frontend","architecture","tests","multi-frontend"],"agendas":[{"id":"AGD-0067","file":"AGD-0067-multi-frontend-architectural-tests.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
{"type":"discussion","id":"DSC-0064","status":"open","ticket":"multi-frontend-architectural-tests","title":"Testes arquiteturais para fronteiras multi-frontend","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","studio","frontend","architecture","tests","multi-frontend"],"agendas":[{"id":"AGD-0067","file":"AGD-0067-multi-frontend-architectural-tests.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
||||||
{"type":"discussion","id":"DSC-0063","status":"open","ticket":"multi-frontend-synthetic-test-frontend","title":"Frontend sintetico de teste para provar neutralidade do pipeline","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","frontend","tests","backend","multi-frontend"],"agendas":[{"id":"AGD-0066","file":"AGD-0066-multi-frontend-synthetic-test-frontend.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
{"type":"discussion","id":"DSC-0063","status":"open","ticket":"multi-frontend-synthetic-test-frontend","title":"Frontend sintetico de teste para provar neutralidade do pipeline","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","frontend","tests","backend","multi-frontend"],"agendas":[{"id":"AGD-0066","file":"AGD-0066-multi-frontend-synthetic-test-frontend.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
||||||
@ -9,7 +9,7 @@
|
|||||||
{"type":"discussion","id":"DSC-0058","status":"open","ticket":"multi-frontend-serializable-ir","title":"Manter a IR comum serializavel por design","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","ir","backend","serialization","multi-frontend"],"agendas":[{"id":"AGD-0061","file":"AGD-0061-multi-frontend-serializable-ir.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
{"type":"discussion","id":"DSC-0058","status":"open","ticket":"multi-frontend-serializable-ir","title":"Manter a IR comum serializavel por design","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","ir","backend","serialization","multi-frontend"],"agendas":[{"id":"AGD-0061","file":"AGD-0061-multi-frontend-serializable-ir.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
||||||
{"type":"discussion","id":"DSC-0057","status":"open","ticket":"multi-frontend-frontend-backend-contract","title":"Estabilizar contrato entre frontend e backend comum","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","compiler-pbs","ir","backend","multi-frontend"],"agendas":[{"id":"AGD-0060","file":"AGD-0060-multi-frontend-frontend-backend-contract.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
{"type":"discussion","id":"DSC-0057","status":"open","ticket":"multi-frontend-frontend-backend-contract","title":"Estabilizar contrato entre frontend e backend comum","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","compiler-pbs","ir","backend","multi-frontend"],"agendas":[{"id":"AGD-0060","file":"AGD-0060-multi-frontend-frontend-backend-contract.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
||||||
{"type":"discussion","id":"DSC-0056","status":"open","ticket":"multi-frontend-remove-pbs-branches","title":"Remover verificacoes explicitas de PBS do codigo comum","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","compiler-pbs","studio","frontend","coupling","multi-frontend"],"agendas":[{"id":"AGD-0059","file":"AGD-0059-multi-frontend-remove-pbs-branches.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
{"type":"discussion","id":"DSC-0056","status":"open","ticket":"multi-frontend-remove-pbs-branches","title":"Remover verificacoes explicitas de PBS do codigo comum","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","compiler-pbs","studio","frontend","coupling","multi-frontend"],"agendas":[{"id":"AGD-0059","file":"AGD-0059-multi-frontend-remove-pbs-branches.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
||||||
{"type":"discussion","id":"DSC-0055","status":"open","ticket":"multi-frontend-compiler-vs-language-services","title":"Separar compilacao de servicos editoriais de frontend","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","studio","lsp","editor","frontend","multi-frontend"],"agendas":[{"id":"AGD-0058","file":"AGD-0058-multi-frontend-compiler-vs-language-services.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
{"type":"discussion","id":"DSC-0055","status":"done","ticket":"multi-frontend-compiler-vs-language-services","title":"Separar compilacao de servicos editoriais de frontend","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","studio","lsp","editor","frontend","multi-frontend"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0056","file":"discussion/lessons/DSC-0055-multi-frontend-compiler-vs-language-services/LSN-0056-compile-first-frontends-with-optional-editorial-capabilities.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15"}]}
|
||||||
{"type":"discussion","id":"DSC-0054","status":"done","ticket":"multi-frontend-provider-contract","title":"Introduzir provider completo de frontend","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","studio","frontend","registry","multi-frontend"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0055","file":"discussion/lessons/DSC-0054-multi-frontend-provider-contract/LSN-0055-static-frontend-providers-before-plugin-architecture.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15"}]}
|
{"type":"discussion","id":"DSC-0054","status":"done","ticket":"multi-frontend-provider-contract","title":"Introduzir provider completo de frontend","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","studio","frontend","registry","multi-frontend"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0055","file":"discussion/lessons/DSC-0054-multi-frontend-provider-contract/LSN-0055-static-frontend-providers-before-plugin-architecture.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15"}]}
|
||||||
{"type":"discussion","id":"DSC-0053","status":"open","ticket":"pbs-lsp-call-and-type-hierarchy","title":"PBS LSP Call and Type Hierarchy","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["studio","lsp","vscode","compiler-pbs","editor","call-hierarchy","type-hierarchy"],"agendas":[{"id":"AGD-0056","file":"AGD-0056-pbs-lsp-call-and-type-hierarchy.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
{"type":"discussion","id":"DSC-0053","status":"open","ticket":"pbs-lsp-call-and-type-hierarchy","title":"PBS LSP Call and Type Hierarchy","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["studio","lsp","vscode","compiler-pbs","editor","call-hierarchy","type-hierarchy"],"agendas":[{"id":"AGD-0056","file":"AGD-0056-pbs-lsp-call-and-type-hierarchy.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
||||||
{"type":"discussion","id":"DSC-0052","status":"open","ticket":"pbs-lsp-document-links","title":"PBS LSP Document Links","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["studio","lsp","vscode","compiler-pbs","editor","document-links","imports"],"agendas":[{"id":"AGD-0055","file":"AGD-0055-pbs-lsp-document-links.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
{"type":"discussion","id":"DSC-0052","status":"open","ticket":"pbs-lsp-document-links","title":"PBS LSP Document Links","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["studio","lsp","vscode","compiler-pbs","editor","document-links","imports"],"agendas":[{"id":"AGD-0055","file":"AGD-0055-pbs-lsp-document-links.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
||||||
|
|||||||
@ -0,0 +1,88 @@
|
|||||||
|
# Compile-first frontends with optional editorial capabilities
|
||||||
|
|
||||||
|
## Original Problem
|
||||||
|
|
||||||
|
Prometeu is moving from a PBS-centered compiler/editor stack toward a multi-frontend compiler. PBS already provides compilation plus rich editor assistance, so it was easy for shared code, LSP, or Studio consumers to assume that every frontend can provide diagnostics, semantic tokens, completion, hover, and signature help as one package.
|
||||||
|
|
||||||
|
That assumption makes future frontend adoption too expensive. A new frontend must be able to compile before it has a complete IDE experience.
|
||||||
|
|
||||||
|
## Consolidated Decision
|
||||||
|
|
||||||
|
The frontend contract is compile-first.
|
||||||
|
|
||||||
|
Every frontend must provide a compiler capability through `FrontendProvider.compiler()`. Editorial services are optional and live behind `FrontendProvider.languageService()`.
|
||||||
|
|
||||||
|
A frontend that can compile but has no language service is valid. Missing editor assistance must not block `analyze`, `compile`, `build`, backend lowering, bytecode emission, verification, or artifact writing.
|
||||||
|
|
||||||
|
LSP and Studio consumers must query editorial capability before invoking editor-specific behavior. Absence of a capability is a normal state, not a compiler failure.
|
||||||
|
|
||||||
|
## Final Implementation
|
||||||
|
|
||||||
|
The compiler pipeline spec now defines the provider boundary explicitly:
|
||||||
|
|
||||||
|
- `compiler()` is the only required executable frontend capability.
|
||||||
|
- `languageService()` is optional.
|
||||||
|
- editor capabilities such as completion, hover, definition, signature help, semantic tokens, formatting, rename, and code actions are optional unless a future accepted decision narrows the requirement.
|
||||||
|
- absent editor capabilities have deterministic fallback behavior.
|
||||||
|
- compiler diagnostics and editor diagnostics are separate ownership surfaces.
|
||||||
|
|
||||||
|
PBS keeps its rich editor assistance, but now exposes it through `PBSFrontendLanguageService`, returned by `PBSFrontendProvider.languageService()`.
|
||||||
|
|
||||||
|
The LSP bridge resolves the active `FrontendProvider` and checks for `languageService()` before using editor behavior. For now, PBS remains an adapter-specific implementation because the common language-service DTOs are not yet generalized. That is acceptable because the important boundary is already enforced: the LSP no longer assumes that compiler support implies editor support.
|
||||||
|
|
||||||
|
Conformance coverage now includes:
|
||||||
|
|
||||||
|
- a compile-only provider fixture with no language service;
|
||||||
|
- LSP neutral responses when editor capability is absent;
|
||||||
|
- PBS regression coverage for existing completion, hover, signature help, semantic tokens, and documentation behavior;
|
||||||
|
- architecture tests preventing common modules from directly instantiating PBS editor support.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
A compile-only frontend should look like this at the provider boundary:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public final class ExampleFrontendProvider implements FrontendProvider {
|
||||||
|
@Override
|
||||||
|
public FrontendSpec specification() {
|
||||||
|
return EXAMPLE_SPEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FrontendPhaseService compiler() {
|
||||||
|
return exampleCompiler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
It does not need to override `languageService()`. The default empty optional is a valid frontend state.
|
||||||
|
|
||||||
|
An editor consumer should treat missing services as a normal fallback:
|
||||||
|
|
||||||
|
```java
|
||||||
|
return provider.languageService()
|
||||||
|
.filter(ExpectedLanguageService.class::isInstance)
|
||||||
|
.map(ExpectedLanguageService.class::cast)
|
||||||
|
.map(service -> service.completion(...))
|
||||||
|
.orElseGet(List::of);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pitfalls
|
||||||
|
|
||||||
|
Do not use PBS as the minimum frontend contract. PBS is the first rich frontend, not the definition of frontend validity.
|
||||||
|
|
||||||
|
Do not make editor diagnostics a prerequisite for compilation. Build/analyze diagnostics are compiler output; live editor diagnostics may reuse compiler results but have different latency, cancellation, overlay, and UX concerns.
|
||||||
|
|
||||||
|
Do not split the language service into many top-level interfaces before implementation pressure proves a real lifecycle or ownership boundary. The current contract intentionally keeps an aggregated optional language-service surface.
|
||||||
|
|
||||||
|
Do not call PBS editorial classes directly from common compiler, LSP, Studio, or app code. Go through `FrontendProvider.languageService()` and adapter-local capability checks.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- Spec: `docs/specs/compiler/23. Compiler Pipeline Entry Points Specification.md`
|
||||||
|
- Provider API: `prometeu-compiler/prometeu-frontend-api/src/main/java/p/studio/compiler/services/FrontendProvider.java`
|
||||||
|
- PBS language service: `prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/PBSFrontendLanguageService.java`
|
||||||
|
- LSP bridge: `prometeu-lsp/prometeu-lsp-v1/src/main/java/p/studio/lsp/services/compiler/CompilerLanguageServiceBridge.java`
|
||||||
|
- Conformance tests:
|
||||||
|
- `prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/specs/FrontendProviderBoundaryTest.java`
|
||||||
|
- `prometeu-lsp/prometeu-lsp-v1/src/test/java/p/studio/lsp/services/compiler/CompilerLanguageServiceBridgeTest.java`
|
||||||
@ -1,78 +0,0 @@
|
|||||||
---
|
|
||||||
id: AGD-0058
|
|
||||||
ticket: multi-frontend-compiler-vs-language-services
|
|
||||||
title: Separar compilacao de servicos editoriais de frontend
|
|
||||||
status: open
|
|
||||||
created: 2026-07-15
|
|
||||||
resolved:
|
|
||||||
decision:
|
|
||||||
tags: [compiler, compiler-general, studio, lsp, editor, frontend, multi-frontend]
|
|
||||||
---
|
|
||||||
|
|
||||||
# Agenda - Separar compilacao de servicos editoriais
|
|
||||||
|
|
||||||
## Objetivo
|
|
||||||
|
|
||||||
Domain owner: `compiler/general`, com impacto em `studio/lsp`.
|
|
||||||
|
|
||||||
Separar o contrato necessario para build do contrato necessario para IDE, permitindo frontends progressivos: compilacao, diagnostics/highlighting e experiencia editorial completa.
|
|
||||||
|
|
||||||
## Contexto atual
|
|
||||||
|
|
||||||
O PBS fornece compilacao e varias superficies editoriais. Historicamente o Studio e o LSP podem assumir que semantic tokens, diagnostics e navegação vêm do mesmo servico PBS.
|
|
||||||
|
|
||||||
## Escopo
|
|
||||||
|
|
||||||
- Identificar interfaces atuais de compilacao e editor.
|
|
||||||
- Definir capabilities opcionais para servicos editoriais.
|
|
||||||
- Garantir que ausencia de completion, hover, definition ou semantic tokens nao invalide um frontend compilavel.
|
|
||||||
|
|
||||||
## Fora de escopo
|
|
||||||
|
|
||||||
- Implementar novas features editoriais.
|
|
||||||
- Redesenhar todo o LSP.
|
|
||||||
- Criar frontend real sem recursos editoriais.
|
|
||||||
|
|
||||||
## Arquivos e componentes a inspecionar
|
|
||||||
|
|
||||||
- `prometeu-lsp/prometeu-lsp-api/...`
|
|
||||||
- `prometeu-lsp/...`
|
|
||||||
- `prometeu-studio/src/main/java/p/studio/...editor...`
|
|
||||||
- `prometeu-compiler/frontends/prometeu-frontend-pbs/...`
|
|
||||||
- `prometeu-compiler/prometeu-build-pipeline/...`
|
|
||||||
|
|
||||||
## Alteracoes propostas
|
|
||||||
|
|
||||||
Opcao A: `FrontendProvider` entrega `FrontendCompiler` obrigatorio e `Optional<FrontendLanguageService>`.
|
|
||||||
|
|
||||||
Opcao B: dividir language service em interfaces menores, como diagnostics, completion, hover, definition e semantic tokens.
|
|
||||||
|
|
||||||
Recomendacao inicial: mapear primeiro os consumidores. Se o LSP ja resolve features separadamente, usar interfaces menores; caso contrario, um language service opcional com capabilities pode ser suficiente.
|
|
||||||
|
|
||||||
## Estrategia de implementacao
|
|
||||||
|
|
||||||
Caracterizar o comportamento PBS atual, definir fallback para recurso ausente e migrar chamadas editoriais para consulta de capability antes de invocar servico.
|
|
||||||
|
|
||||||
## Testes necessarios
|
|
||||||
|
|
||||||
- Frontend provider compilavel sem language service nao quebra build.
|
|
||||||
- LSP/Studio tratam feature ausente como resposta vazia ou unsupported documentado.
|
|
||||||
- PBS continua expondo as mesmas respostas editoriais.
|
|
||||||
|
|
||||||
## Criterios de aceitacao
|
|
||||||
|
|
||||||
- Compilacao nao depende de APIs de IDE.
|
|
||||||
- Recursos editoriais ausentes sao opcionais.
|
|
||||||
- O contrato permite os niveis 1, 2 e 3 descritos no alinhamento.
|
|
||||||
|
|
||||||
## Riscos
|
|
||||||
|
|
||||||
- Introduzir `Optional` em excesso sem uma politica consistente de fallback.
|
|
||||||
- Acoplar diagnostics de build e diagnostics editoriais sem separar latencia e escopo.
|
|
||||||
|
|
||||||
## Decisoes que devem ser registradas
|
|
||||||
|
|
||||||
- Granularidade final das interfaces editoriais.
|
|
||||||
- Comportamento padrao para capability ausente.
|
|
||||||
- Relacao entre diagnostics de compilacao e diagnostics de editor.
|
|
||||||
|
|
||||||
@ -241,7 +241,37 @@ At minimum, each provider MUST expose:
|
|||||||
2. `compiler()`, returning the compiler-facing frontend service used by the shared build pipeline;
|
2. `compiler()`, returning the compiler-facing frontend service used by the shared build pipeline;
|
||||||
3. `languageService()`, returning an optional editor-facing language-service capability for tooling consumers.
|
3. `languageService()`, returning an optional editor-facing language-service capability for tooling consumers.
|
||||||
|
|
||||||
`languageService()` MUST be optional. A frontend that supports compilation but does not provide editor services MUST remain a valid provider.
|
`compiler()` is the only required executable frontend capability. It MUST be sufficient for `analyze`, `compile`, `build`, and backend-facing pipeline flows.
|
||||||
|
|
||||||
|
`languageService()` MUST be optional. A frontend that supports compilation but does not provide editor services MUST remain a valid provider. Absence of `languageService()` MUST NOT prevent project analysis, compilation, backend lowering, bytecode emission, verification, or artifact writing.
|
||||||
|
|
||||||
|
The first shared editor-facing contract SHOULD remain an aggregated language-service capability surface. The common compiler contract MUST NOT require separate top-level service interfaces for each editor feature until a future accepted decision and plan identify a real lifecycle, ownership, or testing boundary that requires that split.
|
||||||
|
|
||||||
|
When exposed, editor-facing capabilities MAY include:
|
||||||
|
|
||||||
|
1. diagnostics intended for live editor sessions,
|
||||||
|
2. completion,
|
||||||
|
3. hover,
|
||||||
|
4. definition and navigation,
|
||||||
|
5. signature help,
|
||||||
|
6. semantic tokens for a live document,
|
||||||
|
7. formatting,
|
||||||
|
8. rename,
|
||||||
|
9. code actions,
|
||||||
|
10. and other host-facing editor assistance.
|
||||||
|
|
||||||
|
These capabilities are optional unless a future frontend- or host-specific decision makes one of them mandatory for a narrower surface. Tooling consumers MUST query capability availability before invoking editor-specific behavior.
|
||||||
|
|
||||||
|
Absent editor capabilities MUST have deterministic fallback behavior at tooling boundaries:
|
||||||
|
|
||||||
|
1. collection-shaped responses SHOULD be empty;
|
||||||
|
2. scalar optional responses SHOULD be absent or use a documented neutral response;
|
||||||
|
3. host protocols that can represent unsupported operations SHOULD do so explicitly;
|
||||||
|
4. and absent editor capability MUST NOT be reported as a compiler failure.
|
||||||
|
|
||||||
|
Compiler diagnostics and editor diagnostics are distinct ownership surfaces. Diagnostics returned by `analyze`, `compile`, and `build` are compiler contract output. Editor diagnostics MAY reuse compiler analysis results, live overlays, caches, or cancellation-aware tooling state, but a frontend MUST NOT be required to provide editor diagnostics in order to compile.
|
||||||
|
|
||||||
|
`FrontendSpec` remains the source of static frontend-owned presentation metadata such as semantic vocabularies, host projections, and visual themes. Producing semantic tokens for a live document is an optional editor-facing capability; the existence of static presentation metadata MUST NOT imply that every frontend can provide live semantic-token results.
|
||||||
|
|
||||||
The frontend registry MUST resolve providers by `languageId`. A lookup for an unknown `languageId` MUST fail explicitly with a diagnostic-friendly error. Unknown languages MUST NOT silently fall back to PBS or to any other frontend.
|
The frontend registry MUST resolve providers by `languageId`. A lookup for an unknown `languageId` MUST fail explicitly with a diagnostic-friendly error. Unknown languages MUST NOT silently fall back to PBS or to any other frontend.
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,62 @@
|
|||||||
|
package p.studio.compiler;
|
||||||
|
|
||||||
|
import p.studio.compiler.messages.FESurfaceContext;
|
||||||
|
import p.studio.compiler.models.FrontendSemanticToken;
|
||||||
|
import p.studio.compiler.pbs.ast.PbsAst;
|
||||||
|
import p.studio.compiler.pbs.semantics.PbsEditorialCompletionCandidate;
|
||||||
|
import p.studio.compiler.pbs.semantics.PbsEditorialResolvedSymbol;
|
||||||
|
import p.studio.compiler.pbs.semantics.PbsEditorialSignatureHelp;
|
||||||
|
import p.studio.compiler.pbs.semantics.PbsEditorialSupportService;
|
||||||
|
import p.studio.compiler.services.FrontendLanguageService;
|
||||||
|
import p.studio.utilities.structures.ReadOnlyList;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public final class PBSFrontendLanguageService implements FrontendLanguageService {
|
||||||
|
private final PbsEditorialSupportService editorialSupportService;
|
||||||
|
private final PBSSemanticTokenProvider semanticTokenProvider;
|
||||||
|
|
||||||
|
public PBSFrontendLanguageService() {
|
||||||
|
this(new PbsEditorialSupportService(), new PBSSemanticTokenProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
PBSFrontendLanguageService(
|
||||||
|
final PbsEditorialSupportService editorialSupportService,
|
||||||
|
final PBSSemanticTokenProvider semanticTokenProvider) {
|
||||||
|
this.editorialSupportService = Objects.requireNonNull(editorialSupportService, "editorialSupportService");
|
||||||
|
this.semanticTokenProvider = Objects.requireNonNull(semanticTokenProvider, "semanticTokenProvider");
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PbsEditorialCompletionCandidate> completion(
|
||||||
|
final String sourceText,
|
||||||
|
final PbsAst.File ast,
|
||||||
|
final ReadOnlyList<PbsAst.TopDecl> supplementalTopDecls,
|
||||||
|
final FESurfaceContext feSurfaceContext,
|
||||||
|
final int offset) {
|
||||||
|
return editorialSupportService.completion(sourceText, ast, supplementalTopDecls, feSurfaceContext, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<PbsEditorialResolvedSymbol> hover(
|
||||||
|
final String sourceText,
|
||||||
|
final PbsAst.File ast,
|
||||||
|
final ReadOnlyList<PbsAst.TopDecl> supplementalTopDecls,
|
||||||
|
final FESurfaceContext feSurfaceContext,
|
||||||
|
final int offset) {
|
||||||
|
return editorialSupportService.hover(sourceText, ast, supplementalTopDecls, feSurfaceContext, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<PbsEditorialSignatureHelp> signatureHelp(
|
||||||
|
final String sourceText,
|
||||||
|
final PbsAst.File ast,
|
||||||
|
final ReadOnlyList<PbsAst.TopDecl> supplementalTopDecls,
|
||||||
|
final FESurfaceContext feSurfaceContext,
|
||||||
|
final int offset) {
|
||||||
|
return editorialSupportService.signatureHelp(sourceText, ast, supplementalTopDecls, feSurfaceContext, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<FrontendSemanticToken> semanticTokens(final String documentText) {
|
||||||
|
return semanticTokenProvider.tokenize(documentText == null ? "" : documentText);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,19 +1,30 @@
|
|||||||
package p.studio.compiler;
|
package p.studio.compiler;
|
||||||
|
|
||||||
import p.studio.compiler.models.FrontendSpec;
|
import p.studio.compiler.models.FrontendSpec;
|
||||||
|
import p.studio.compiler.services.FrontendLanguageService;
|
||||||
import p.studio.compiler.services.FrontendPhaseService;
|
import p.studio.compiler.services.FrontendPhaseService;
|
||||||
import p.studio.compiler.services.FrontendProvider;
|
import p.studio.compiler.services.FrontendProvider;
|
||||||
import p.studio.compiler.services.PBSFrontendPhaseService;
|
import p.studio.compiler.services.PBSFrontendPhaseService;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public final class PBSFrontendProvider implements FrontendProvider {
|
public final class PBSFrontendProvider implements FrontendProvider {
|
||||||
private final FrontendPhaseService compiler;
|
private final FrontendPhaseService compiler;
|
||||||
|
private final PBSFrontendLanguageService languageService;
|
||||||
|
|
||||||
public PBSFrontendProvider() {
|
public PBSFrontendProvider() {
|
||||||
this(new PBSFrontendPhaseService());
|
this(new PBSFrontendPhaseService(), new PBSFrontendLanguageService());
|
||||||
}
|
}
|
||||||
|
|
||||||
PBSFrontendProvider(final FrontendPhaseService compiler) {
|
PBSFrontendProvider(final FrontendPhaseService compiler) {
|
||||||
|
this(compiler, new PBSFrontendLanguageService());
|
||||||
|
}
|
||||||
|
|
||||||
|
PBSFrontendProvider(
|
||||||
|
final FrontendPhaseService compiler,
|
||||||
|
final PBSFrontendLanguageService languageService) {
|
||||||
this.compiler = compiler;
|
this.compiler = compiler;
|
||||||
|
this.languageService = languageService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -25,4 +36,9 @@ public final class PBSFrontendProvider implements FrontendProvider {
|
|||||||
public FrontendPhaseService compiler() {
|
public FrontendPhaseService compiler() {
|
||||||
return compiler;
|
return compiler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<FrontendLanguageService> languageService() {
|
||||||
|
return Optional.of(languageService);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,8 +4,8 @@ import org.junit.jupiter.api.Test;
|
|||||||
import p.studio.compiler.services.PBSFrontendPhaseService;
|
import p.studio.compiler.services.PBSFrontendPhaseService;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
|
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
class PBSFrontendProviderTest {
|
class PBSFrontendProviderTest {
|
||||||
|
|
||||||
@ -15,6 +15,7 @@ class PBSFrontendProviderTest {
|
|||||||
|
|
||||||
assertEquals(PBSDefinitions.PBS.getLanguageId(), provider.specification().getLanguageId());
|
assertEquals(PBSDefinitions.PBS.getLanguageId(), provider.specification().getLanguageId());
|
||||||
assertInstanceOf(PBSFrontendPhaseService.class, provider.compiler());
|
assertInstanceOf(PBSFrontendPhaseService.class, provider.compiler());
|
||||||
assertFalse(provider.languageService().isPresent());
|
assertTrue(provider.languageService().isPresent());
|
||||||
|
assertInstanceOf(PBSFrontendLanguageService.class, provider.languageService().orElseThrow());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,23 @@
|
|||||||
package p.studio.compiler.specs;
|
package p.studio.compiler.specs;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import p.studio.compiler.messages.BuildingIssueSink;
|
||||||
|
import p.studio.compiler.messages.FrontendPhaseContext;
|
||||||
|
import p.studio.compiler.models.FrontendSpec;
|
||||||
|
import p.studio.compiler.models.IRBackend;
|
||||||
|
import p.studio.compiler.services.FrontendPhaseService;
|
||||||
|
import p.studio.compiler.services.FrontendProvider;
|
||||||
|
import p.studio.compiler.source.diagnostics.DiagnosticSink;
|
||||||
|
import p.studio.utilities.logs.LogAggregator;
|
||||||
|
import p.studio.utilities.structures.ReadOnlySet;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
class FrontendProviderBoundaryTest {
|
class FrontendProviderBoundaryTest {
|
||||||
@ -37,6 +48,45 @@ class FrontendProviderBoundaryTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void commonModulesMustNotInstantiatePbsEditorialSupportServiceDirectly() throws IOException {
|
||||||
|
final var repoRoot = findRepoRoot(Path.of("").toAbsolutePath().normalize());
|
||||||
|
final var sourceRoots = List.of(
|
||||||
|
repoRoot.resolve("prometeu-compiler/prometeu-build-pipeline/src/main/java"),
|
||||||
|
repoRoot.resolve("prometeu-compiler/prometeu-deps/src/main/java"),
|
||||||
|
repoRoot.resolve("prometeu-lsp/prometeu-lsp-v1/src/main/java"),
|
||||||
|
repoRoot.resolve("prometeu-studio/src/main/java"),
|
||||||
|
repoRoot.resolve("prometeu-app/src/main/java"));
|
||||||
|
|
||||||
|
for (final var sourceRoot : sourceRoots) {
|
||||||
|
if (!Files.exists(sourceRoot)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try (final var files = Files.walk(sourceRoot)) {
|
||||||
|
final var violations = files
|
||||||
|
.filter(path -> path.toString().endsWith(".java"))
|
||||||
|
.filter(path -> contains(path, "new PbsEditorialSupportService("))
|
||||||
|
.toList();
|
||||||
|
if (!violations.isEmpty()) {
|
||||||
|
fail("common modules must resolve PBS editor behavior through FrontendProvider.languageService(): " + violations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void compileOnlyFrontendProviderMayOmitLanguageService() {
|
||||||
|
final FrontendProvider provider = new CompileOnlyFrontendProvider();
|
||||||
|
|
||||||
|
assertTrue(provider.languageService().isEmpty());
|
||||||
|
assertNotNull(provider.compiler().compile(
|
||||||
|
null,
|
||||||
|
DiagnosticSink.empty(),
|
||||||
|
LogAggregator.empty(),
|
||||||
|
BuildingIssueSink.empty()));
|
||||||
|
}
|
||||||
|
|
||||||
private boolean contains(final Path path, final String needle) {
|
private boolean contains(final Path path, final String needle) {
|
||||||
try {
|
try {
|
||||||
return Files.readString(path).contains(needle);
|
return Files.readString(path).contains(needle);
|
||||||
@ -56,4 +106,34 @@ class FrontendProviderBoundaryTest {
|
|||||||
fail("unable to locate repository root from " + start);
|
fail("unable to locate repository root from " + start);
|
||||||
return start;
|
return start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class CompileOnlyFrontendProvider implements FrontendProvider {
|
||||||
|
private final FrontendSpec specification = FrontendSpec.builder()
|
||||||
|
.languageId("compile-only")
|
||||||
|
.allowedExtensions(ReadOnlySet.from("co"))
|
||||||
|
.sourceRoots(ReadOnlySet.from("src"))
|
||||||
|
.build();
|
||||||
|
private final FrontendPhaseService compiler = new CompileOnlyFrontendPhaseService();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FrontendSpec specification() {
|
||||||
|
return specification;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FrontendPhaseService compiler() {
|
||||||
|
return compiler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class CompileOnlyFrontendPhaseService implements FrontendPhaseService {
|
||||||
|
@Override
|
||||||
|
public IRBackend compile(
|
||||||
|
final FrontendPhaseContext request,
|
||||||
|
final DiagnosticSink diagnostics,
|
||||||
|
final LogAggregator logs,
|
||||||
|
final BuildingIssueSink issues) {
|
||||||
|
return IRBackend.builder().build();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,9 +2,11 @@ package p.studio.lsp.services.compiler;
|
|||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import p.studio.compiler.FrontendRegistryService;
|
import p.studio.compiler.FrontendRegistryService;
|
||||||
|
import p.studio.compiler.PBSFrontendLanguageService;
|
||||||
import p.studio.compiler.messages.*;
|
import p.studio.compiler.messages.*;
|
||||||
import p.studio.compiler.models.*;
|
import p.studio.compiler.models.*;
|
||||||
import p.studio.compiler.pbs.semantics.*;
|
import p.studio.compiler.pbs.semantics.*;
|
||||||
|
import p.studio.compiler.services.FrontendProvider;
|
||||||
import p.studio.compiler.services.PBSFrontendPhaseService;
|
import p.studio.compiler.services.PBSFrontendPhaseService;
|
||||||
import p.studio.compiler.source.identifiers.FileId;
|
import p.studio.compiler.source.identifiers.FileId;
|
||||||
import p.studio.compiler.utilities.SourceProviderFactory;
|
import p.studio.compiler.utilities.SourceProviderFactory;
|
||||||
@ -18,10 +20,19 @@ import p.studio.utilities.structures.ReadOnlyList;
|
|||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
public final class CompilerLanguageServiceBridge implements LanguageServiceBridge {
|
public final class CompilerLanguageServiceBridge implements LanguageServiceBridge {
|
||||||
private final AssetSurfaceContextLoader assetSurfaceContextLoader = new AssetSurfaceContextLoader();
|
private final AssetSurfaceContextLoader assetSurfaceContextLoader = new AssetSurfaceContextLoader();
|
||||||
private final PbsEditorialSupportService editorialSupportService = new PbsEditorialSupportService();
|
private final Function<String, FrontendProvider> frontendProviderResolver;
|
||||||
|
|
||||||
|
public CompilerLanguageServiceBridge() {
|
||||||
|
this(FrontendRegistryService::require);
|
||||||
|
}
|
||||||
|
|
||||||
|
CompilerLanguageServiceBridge(final Function<String, FrontendProvider> frontendProviderResolver) {
|
||||||
|
this.frontendProviderResolver = Objects.requireNonNull(frontendProviderResolver, "frontendProviderResolver");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BaselineServerDescription describeServer(final LspProjectContext context) {
|
public BaselineServerDescription describeServer(final LspProjectContext context) {
|
||||||
@ -61,10 +72,14 @@ public final class CompilerLanguageServiceBridge implements LanguageServiceBridg
|
|||||||
final int line,
|
final int line,
|
||||||
final int character) {
|
final int character) {
|
||||||
Objects.requireNonNull(context, "context");
|
Objects.requireNonNull(context, "context");
|
||||||
|
final Optional<PBSFrontendLanguageService> languageService = pbsLanguageService(context);
|
||||||
|
if (languageService.isEmpty()) {
|
||||||
|
return new BaselineCompletion(false, List.of());
|
||||||
|
}
|
||||||
return editorialDocument(context, documentUri, text)
|
return editorialDocument(context, documentUri, text)
|
||||||
.map(document -> {
|
.map(document -> {
|
||||||
final int offset = new DocumentPositionMapper(document.text()).byteOffsetOf(line, character);
|
final int offset = new DocumentPositionMapper(document.text()).byteOffsetOf(line, character);
|
||||||
final List<PbsEditorialCompletionCandidate> candidates = editorialSupportService.completion(
|
final List<PbsEditorialCompletionCandidate> candidates = languageService.orElseThrow().completion(
|
||||||
document.text(),
|
document.text(),
|
||||||
document.ast(),
|
document.ast(),
|
||||||
document.supplementalTopDecls(),
|
document.supplementalTopDecls(),
|
||||||
@ -85,10 +100,14 @@ public final class CompilerLanguageServiceBridge implements LanguageServiceBridg
|
|||||||
final int line,
|
final int line,
|
||||||
final int character) {
|
final int character) {
|
||||||
Objects.requireNonNull(context, "context");
|
Objects.requireNonNull(context, "context");
|
||||||
|
final Optional<PBSFrontendLanguageService> languageService = pbsLanguageService(context);
|
||||||
|
if (languageService.isEmpty()) {
|
||||||
|
return new BaselineHover("No symbol information.");
|
||||||
|
}
|
||||||
return editorialDocument(context, documentUri, text)
|
return editorialDocument(context, documentUri, text)
|
||||||
.flatMap(document -> {
|
.flatMap(document -> {
|
||||||
final int offset = new DocumentPositionMapper(document.text()).byteOffsetOf(line, character);
|
final int offset = new DocumentPositionMapper(document.text()).byteOffsetOf(line, character);
|
||||||
return editorialSupportService.hover(
|
return languageService.orElseThrow().hover(
|
||||||
document.text(),
|
document.text(),
|
||||||
document.ast(),
|
document.ast(),
|
||||||
document.supplementalTopDecls(),
|
document.supplementalTopDecls(),
|
||||||
@ -107,10 +126,14 @@ public final class CompilerLanguageServiceBridge implements LanguageServiceBridg
|
|||||||
final int line,
|
final int line,
|
||||||
final int character) {
|
final int character) {
|
||||||
Objects.requireNonNull(context, "context");
|
Objects.requireNonNull(context, "context");
|
||||||
|
final Optional<PBSFrontendLanguageService> languageService = pbsLanguageService(context);
|
||||||
|
if (languageService.isEmpty()) {
|
||||||
|
return new BaselineSignatureHelp(List.of(), 0, 0);
|
||||||
|
}
|
||||||
return editorialDocument(context, documentUri, text)
|
return editorialDocument(context, documentUri, text)
|
||||||
.flatMap(document -> {
|
.flatMap(document -> {
|
||||||
final int offset = new DocumentPositionMapper(document.text()).byteOffsetOf(line, character);
|
final int offset = new DocumentPositionMapper(document.text()).byteOffsetOf(line, character);
|
||||||
return editorialSupportService.signatureHelp(
|
return languageService.orElseThrow().signatureHelp(
|
||||||
document.text(),
|
document.text(),
|
||||||
document.ast(),
|
document.ast(),
|
||||||
document.supplementalTopDecls(),
|
document.supplementalTopDecls(),
|
||||||
@ -133,9 +156,11 @@ public final class CompilerLanguageServiceBridge implements LanguageServiceBridg
|
|||||||
final String text) {
|
final String text) {
|
||||||
Objects.requireNonNull(context, "context");
|
Objects.requireNonNull(context, "context");
|
||||||
final String effectiveText = text == null ? "" : text;
|
final String effectiveText = text == null ? "" : text;
|
||||||
final var frontend = frontendSpec(context);
|
final var frontend = frontendProvider(context).specification();
|
||||||
final FrontendSemanticPresentationSpec presentation = frontend.getSemanticPresentation();
|
final FrontendSemanticPresentationSpec presentation = frontend.getSemanticPresentation();
|
||||||
final List<FrontendSemanticToken> tokens = frontend.getSemanticTokenProvider().tokenize(effectiveText);
|
final List<FrontendSemanticToken> tokens = pbsLanguageService(context)
|
||||||
|
.map(languageService -> languageService.semanticTokens(effectiveText))
|
||||||
|
.orElseGet(List::of);
|
||||||
final var positionMapper = new DocumentPositionMapper(effectiveText);
|
final var positionMapper = new DocumentPositionMapper(effectiveText);
|
||||||
final var semanticTokens = new ArrayList<BaselineSemanticToken>();
|
final var semanticTokens = new ArrayList<BaselineSemanticToken>();
|
||||||
|
|
||||||
@ -177,9 +202,6 @@ public final class CompilerLanguageServiceBridge implements LanguageServiceBridg
|
|||||||
final LspProjectContext context,
|
final LspProjectContext context,
|
||||||
final String documentUri,
|
final String documentUri,
|
||||||
final String text) {
|
final String text) {
|
||||||
if (!"pbs".equals(context.languageId())) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
final Path documentPath = normalizeDocumentPath(documentUri);
|
final Path documentPath = normalizeDocumentPath(documentUri);
|
||||||
final BuilderPipelineContext pipelineContext = BuilderPipelineContext.fromConfig(
|
final BuilderPipelineContext pipelineContext = BuilderPipelineContext.fromConfig(
|
||||||
new BuilderPipelineConfig(
|
new BuilderPipelineConfig(
|
||||||
@ -335,8 +357,17 @@ public final class CompilerLanguageServiceBridge implements LanguageServiceBridg
|
|||||||
}
|
}
|
||||||
|
|
||||||
private p.studio.compiler.models.FrontendSpec frontendSpec(final LspProjectContext context) {
|
private p.studio.compiler.models.FrontendSpec frontendSpec(final LspProjectContext context) {
|
||||||
return FrontendRegistryService.getFrontendSpec(context.languageId())
|
return frontendProvider(context).specification();
|
||||||
.orElseThrow(() -> new IllegalArgumentException("no frontend registered for languageId: " + context.languageId()));
|
}
|
||||||
|
|
||||||
|
private FrontendProvider frontendProvider(final LspProjectContext context) {
|
||||||
|
return frontendProviderResolver.apply(context.languageId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<PBSFrontendLanguageService> pbsLanguageService(final LspProjectContext context) {
|
||||||
|
return frontendProvider(context).languageService()
|
||||||
|
.filter(PBSFrontendLanguageService.class::isInstance)
|
||||||
|
.map(PBSFrontendLanguageService.class::cast);
|
||||||
}
|
}
|
||||||
|
|
||||||
private BaselineCompletionItemKind mapCompletionKind(final PbsEditorialSymbolKind kind) {
|
private BaselineCompletionItemKind mapCompletionKind(final PbsEditorialSymbolKind kind) {
|
||||||
|
|||||||
@ -1,8 +1,17 @@
|
|||||||
package p.studio.lsp.services.compiler;
|
package p.studio.lsp.services.compiler;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import p.studio.compiler.messages.BuildingIssueSink;
|
||||||
|
import p.studio.compiler.messages.FrontendPhaseContext;
|
||||||
|
import p.studio.compiler.models.FrontendSpec;
|
||||||
|
import p.studio.compiler.models.IRBackend;
|
||||||
|
import p.studio.compiler.services.FrontendPhaseService;
|
||||||
|
import p.studio.compiler.services.FrontendProvider;
|
||||||
|
import p.studio.compiler.source.diagnostics.DiagnosticSink;
|
||||||
import p.studio.lsp.messages.BaselineIssueSeverity;
|
import p.studio.lsp.messages.BaselineIssueSeverity;
|
||||||
import p.studio.lsp.messages.LspProjectContext;
|
import p.studio.lsp.messages.LspProjectContext;
|
||||||
|
import p.studio.utilities.logs.LogAggregator;
|
||||||
|
import p.studio.utilities.structures.ReadOnlySet;
|
||||||
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@ -12,6 +21,35 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
|||||||
|
|
||||||
class CompilerLanguageServiceBridgeTest {
|
class CompilerLanguageServiceBridgeTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void editorialEndpointsReturnNeutralResponsesWhenLanguageServiceIsAbsent() {
|
||||||
|
final var provider = new CompileOnlyFrontendProvider();
|
||||||
|
final CompilerLanguageServiceBridge bridge = new CompilerLanguageServiceBridge(languageId -> {
|
||||||
|
if (provider.specification().getLanguageId().equals(languageId)) {
|
||||||
|
return provider;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("no frontend registered for languageId: " + languageId);
|
||||||
|
});
|
||||||
|
final LspProjectContext context = new LspProjectContext("main", provider.specification().getLanguageId(), Path.of("."));
|
||||||
|
final String documentUri = Path.of("demo.co").toUri().toString();
|
||||||
|
|
||||||
|
final var completion = bridge.completion(context, documentUri, "fn main() {}", 0, 0);
|
||||||
|
assertFalse(completion.incomplete());
|
||||||
|
assertTrue(completion.items().isEmpty());
|
||||||
|
|
||||||
|
final var hover = bridge.hover(context, documentUri, "fn main() {}", 0, 0);
|
||||||
|
assertEquals("No symbol information.", hover.markdown());
|
||||||
|
|
||||||
|
final var signatureHelp = bridge.signatureHelp(context, documentUri, "fn main() {}", 0, 0);
|
||||||
|
assertTrue(signatureHelp.signatures().isEmpty());
|
||||||
|
assertEquals(0, signatureHelp.activeSignature());
|
||||||
|
assertEquals(0, signatureHelp.activeParameter());
|
||||||
|
|
||||||
|
final var semanticTokens = bridge.semanticTokens(context, documentUri, "fn main() {}");
|
||||||
|
assertTrue(semanticTokens.legend().isEmpty());
|
||||||
|
assertTrue(semanticTokens.tokens().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void analyzeDocumentReturnsCompilerDiagnosticsForOverlayContent() {
|
void analyzeDocumentReturnsCompilerDiagnosticsForOverlayContent() {
|
||||||
final Path projectRoot = findRepoRoot(Path.of("").toAbsolutePath().normalize())
|
final Path projectRoot = findRepoRoot(Path.of("").toAbsolutePath().normalize())
|
||||||
@ -305,4 +343,34 @@ class CompilerLanguageServiceBridgeTest {
|
|||||||
}
|
}
|
||||||
throw new IllegalStateException("unable to locate repository root from " + start);
|
throw new IllegalStateException("unable to locate repository root from " + start);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class CompileOnlyFrontendProvider implements FrontendProvider {
|
||||||
|
private final FrontendSpec specification = FrontendSpec.builder()
|
||||||
|
.languageId("compile-only")
|
||||||
|
.allowedExtensions(ReadOnlySet.from("co"))
|
||||||
|
.sourceRoots(ReadOnlySet.from("src"))
|
||||||
|
.build();
|
||||||
|
private final FrontendPhaseService compiler = new CompileOnlyFrontendPhaseService();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FrontendSpec specification() {
|
||||||
|
return specification;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FrontendPhaseService compiler() {
|
||||||
|
return compiler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class CompileOnlyFrontendPhaseService implements FrontendPhaseService {
|
||||||
|
@Override
|
||||||
|
public IRBackend compile(
|
||||||
|
final FrontendPhaseContext request,
|
||||||
|
final DiagnosticSink diagnostics,
|
||||||
|
final LogAggregator logs,
|
||||||
|
final BuildingIssueSink issues) {
|
||||||
|
return IRBackend.builder().build();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user