From d680504c31af98b70999ac07f34f9db10eac7b8c Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Wed, 15 Jul 2026 07:08:11 +0100 Subject: [PATCH] implements PLN-0097 --- discussion/index.ndjson | 2 +- ...ch-doc-metadata-to-pbs-semantic-symbols.md | 2 +- .../semantics/PbsEditorialSupportService.java | 69 +++++++++++++++++-- .../pbs/semantics/PbsFlowSemanticSupport.java | 43 +++++++++--- .../PbsEditorialSupportServiceTest.java | 43 ++++++++++++ 5 files changed, 143 insertions(+), 16 deletions(-) diff --git a/discussion/index.ndjson b/discussion/index.ndjson index 54b2d66b..3dcf6ac7 100644 --- a/discussion/index.ndjson +++ b/discussion/index.ndjson @@ -1,7 +1,7 @@ {"type":"meta","next_id":{"DSC":39,"AGD":42,"DEC":40,"PLN":103,"LSN":55,"CLSN":1}} {"type":"discussion","id":"DSC-0038","status":"done","ticket":"studio-packer-rgba8888-asset-pipeline","title":"Studio and Packer RGBA8888 Asset Pipeline Alignment","created_at":"2026-05-23","updated_at":"2026-07-14","tags":["studio","packer","assets","glyph-bank","palette","rgba8888","runtime-alignment"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0053","file":"discussion/lessons/DSC-0038-studio-packer-rgba8888-asset-pipeline/LSN-0053-rgba8888-is-the-canonical-studio-packer-palette-contract.md","status":"done","created_at":"2026-07-14","updated_at":"2026-07-14"}]} {"type":"discussion","id":"DSC-0037","status":"done","ticket":"pbs-autocomplete-parameter-names","title":"PBS autocomplete parameter names for stdlib and method calls","created_at":"2026-05-08","updated_at":"2026-05-14","tags":["compiler-pbs","studio","lsp","autocomplete","signature-help","stdlib"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0052","file":"discussion/lessons/DSC-0037-pbs-autocomplete-parameter-names/LSN-0052-canonical-callable-parameter-names-through-pbs-editor-assistance.md","status":"done","created_at":"2026-05-14","updated_at":"2026-05-14"}]} -{"type":"discussion","id":"DSC-0036","status":"in_progress","ticket":"pbs-symbol-documentation-and-hover-markdown","title":"Modelo de documentacao de simbolos em PBS e consumo markdown no hover","created_at":"2026-05-08","updated_at":"2026-07-15","tags":["compiler","compiler-pbs","studio","lsp","vscode","editor","hover","documentation","markdown"],"agendas":[{"id":"AGD-0039","file":"AGD-0039-pbs-symbol-documentation-and-hover-markdown.md","status":"accepted","created_at":"2026-05-08","updated_at":"2026-07-15"}],"decisions":[{"id":"DEC-0039","file":"DEC-0039-pbs-symbol-documentation-with-doc-markdown-text-blocks.md","status":"accepted","created_at":"2026-07-15","updated_at":"2026-07-15","ref_agenda":"AGD-0039"}],"plans":[{"id":"PLN-0092","file":"PLN-0092-specify-pbs-doc-attribute-and-markdown-text-block-syntax.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0093","file":"PLN-0093-implement-pbs-lexer-support-for-documentation-text-blocks.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0094","file":"PLN-0094-extend-pbs-attribute-parser-and-ast-for-doc-markdown-payloads.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0095","file":"PLN-0095-implement-documentation-text-block-normalization.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0096","file":"PLN-0096-validate-doc-attribute-semantics-and-diagnostics.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0097","file":"PLN-0097-attach-doc-metadata-to-pbs-semantic-symbols.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0098","file":"PLN-0098-expose-doc-metadata-through-pbs-editorial-and-lsp-surfaces.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0099","file":"PLN-0099-render-doc-markdown-in-hover-composition.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0100","file":"PLN-0100-author-doc-metadata-for-stdlib-sdk-and-interface-declarations.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0101","file":"PLN-0101-protect-runtime-artifacts-from-doc-metadata-lowering.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0102","file":"PLN-0102-add-end-to-end-doc-documentation-conformance-coverage.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]}],"lessons":[]} +{"type":"discussion","id":"DSC-0036","status":"in_progress","ticket":"pbs-symbol-documentation-and-hover-markdown","title":"Modelo de documentacao de simbolos em PBS e consumo markdown no hover","created_at":"2026-05-08","updated_at":"2026-07-15","tags":["compiler","compiler-pbs","studio","lsp","vscode","editor","hover","documentation","markdown"],"agendas":[{"id":"AGD-0039","file":"AGD-0039-pbs-symbol-documentation-and-hover-markdown.md","status":"accepted","created_at":"2026-05-08","updated_at":"2026-07-15"}],"decisions":[{"id":"DEC-0039","file":"DEC-0039-pbs-symbol-documentation-with-doc-markdown-text-blocks.md","status":"accepted","created_at":"2026-07-15","updated_at":"2026-07-15","ref_agenda":"AGD-0039"}],"plans":[{"id":"PLN-0092","file":"PLN-0092-specify-pbs-doc-attribute-and-markdown-text-block-syntax.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0093","file":"PLN-0093-implement-pbs-lexer-support-for-documentation-text-blocks.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0094","file":"PLN-0094-extend-pbs-attribute-parser-and-ast-for-doc-markdown-payloads.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0095","file":"PLN-0095-implement-documentation-text-block-normalization.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0096","file":"PLN-0096-validate-doc-attribute-semantics-and-diagnostics.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0097","file":"PLN-0097-attach-doc-metadata-to-pbs-semantic-symbols.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0098","file":"PLN-0098-expose-doc-metadata-through-pbs-editorial-and-lsp-surfaces.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0099","file":"PLN-0099-render-doc-markdown-in-hover-composition.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0100","file":"PLN-0100-author-doc-metadata-for-stdlib-sdk-and-interface-declarations.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0101","file":"PLN-0101-protect-runtime-artifacts-from-doc-metadata-lowering.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]},{"id":"PLN-0102","file":"PLN-0102-add-end-to-end-doc-documentation-conformance-coverage.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0039"]}],"lessons":[]} {"type":"discussion","id":"DSC-0035","status":"done","ticket":"pbs-lsp-editor-assistance-wave-1","title":"Wave 1 de assistencia editorial via LSP para PBS no VS Code","created_at":"2026-05-08","updated_at":"2026-05-08","tags":["studio","lsp","vscode","compiler","compiler-pbs","editor","completion","hover","signature-help"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0051","file":"discussion/lessons/DSC-0035-pbs-lsp-editor-assistance-wave-1/LSN-0051-compiler-backed-editor-assistance-for-pbs.md","status":"done","created_at":"2026-05-08","updated_at":"2026-05-08"}]} {"type":"discussion","id":"DSC-0034","status":"done","ticket":"frontend-semantic-host-projection-flexibility","title":"Frontend semantic vocabulary flexibility and declarative host projection","created_at":"2026-05-06","updated_at":"2026-05-07","tags":["compiler","compiler-general","frontend","semantics","vscode","host-projection","lsp"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0048","file":"discussion/lessons/DSC-0034-frontend-semantic-host-projection-flexibility/LSN-0048-frontend-owned-semantic-vocabularies-with-declarative-host-projection.md","status":"done","created_at":"2026-05-07","updated_at":"2026-05-07"}]} {"type":"discussion","id":"DSC-0033","status":"done","ticket":"frontend-visual-theme-spec-and-css-retirement","title":"Frontend visual theme spec and retirement of host-consumed semantic CSS","created_at":"2026-05-06","updated_at":"2026-05-08","tags":["compiler","compiler-general","frontend","presentation","theming","studio","vscode","lsp","pbs"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0050","file":"discussion/lessons/DSC-0033-frontend-visual-theme-spec-and-css-retirement/LSN-0050-frontend-owned-visual-themes-with-structured-contract-and-host-adapters.md","status":"done","created_at":"2026-05-08","updated_at":"2026-05-08"}]} diff --git a/discussion/workflow/plans/PLN-0097-attach-doc-metadata-to-pbs-semantic-symbols.md b/discussion/workflow/plans/PLN-0097-attach-doc-metadata-to-pbs-semantic-symbols.md index a3422ff2..eab3a6af 100644 --- a/discussion/workflow/plans/PLN-0097-attach-doc-metadata-to-pbs-semantic-symbols.md +++ b/discussion/workflow/plans/PLN-0097-attach-doc-metadata-to-pbs-semantic-symbols.md @@ -2,7 +2,7 @@ id: PLN-0097 ticket: pbs-symbol-documentation-and-hover-markdown title: Attach Doc metadata to PBS semantic symbols -status: open +status: done created: 2026-07-15 ref_decisions: [DEC-0039] tags: [compiler, compiler-pbs, studio, lsp, vscode, editor, hover, documentation, markdown] diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsEditorialSupportService.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsEditorialSupportService.java index fc4e65ac..a640ca05 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsEditorialSupportService.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsEditorialSupportService.java @@ -587,7 +587,7 @@ public final class PbsEditorialSupportService { formatCallableDetails(methods), receiverType.name(), signaturesFromCallables(methods), - "")); + documentationFromCallables(methods))); } } if (receiverType.kind() == Kind.SERVICE || receiverType.kind() == Kind.CONTRACT) { @@ -602,7 +602,7 @@ public final class PbsEditorialSupportService { formatCallableDetails(signatures), receiverType.name(), signaturesFromCallables(signatures), - "")); + documentationFromCallables(signatures))); } } return Optional.empty(); @@ -663,7 +663,7 @@ public final class PbsEditorialSupportService { detail, topDeclSymbol.origin(), signatures, - ""); + documentationFromTopDecl(topDeclSymbol.decl())); } private PbsEditorialResolvedSymbol constructorSymbolFromTopDecl( @@ -678,7 +678,68 @@ public final class PbsEditorialSupportService { detail, topDeclSymbol.origin(), signatures, - ""); + documentationFromTopDecl(topDeclSymbol.decl())); + } + + private String documentationFromCallables(final List callables) { + for (final var callable : callables) { + if (!callable.documentation().isBlank()) { + return callable.documentation(); + } + } + return ""; + } + + private String documentationFromTopDecl(final PbsAst.TopDecl topDecl) { + return documentationFromAttributes(attributesOf(topDecl)); + } + + private ReadOnlyList attributesOf(final PbsAst.TopDecl topDecl) { + if (topDecl instanceof PbsAst.FunctionDecl functionDecl) { + return functionDecl.attributes(); + } + if (topDecl instanceof PbsAst.StructDecl structDecl) { + return structDecl.attributes(); + } + if (topDecl instanceof PbsAst.BuiltinTypeDecl builtinTypeDecl) { + return builtinTypeDecl.attributes(); + } + if (topDecl instanceof PbsAst.ServiceDecl serviceDecl) { + return serviceDecl.attributes(); + } + if (topDecl instanceof PbsAst.HostDecl hostDecl) { + return hostDecl.attributes(); + } + if (topDecl instanceof PbsAst.ContractDecl contractDecl) { + return contractDecl.attributes(); + } + if (topDecl instanceof PbsAst.CallbackDecl callbackDecl) { + return callbackDecl.attributes(); + } + if (topDecl instanceof PbsAst.EnumDecl enumDecl) { + return enumDecl.attributes(); + } + if (topDecl instanceof PbsAst.ErrorDecl errorDecl) { + return errorDecl.attributes(); + } + if (topDecl instanceof PbsAst.ConstDecl constDecl) { + return constDecl.attributes(); + } + return ReadOnlyList.empty(); + } + + private String documentationFromAttributes(final ReadOnlyList attributes) { + for (final var attribute : attributes) { + if (!"Doc".equals(attribute.name()) || attribute.arguments().isEmpty()) { + continue; + } + final var argument = attribute.arguments().getFirst(); + if (!"markdown".equals(argument.name()) || !(argument.value() instanceof PbsAst.AttributeDocTextBlockValue docValue)) { + continue; + } + return p.studio.compiler.pbs.PbsDocumentationTextBlockNormalizer.normalizeLexeme(docValue.value()); + } + return ""; } private PbsEditorialCompletionCandidate completionFromTopDecl(final TopDeclSymbol topDeclSymbol) { diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsFlowSemanticSupport.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsFlowSemanticSupport.java index 60132df1..d0f9036e 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsFlowSemanticSupport.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsFlowSemanticSupport.java @@ -146,7 +146,8 @@ final class PbsFlowSemanticSupport { List parameters, List inputTypes, TypeView outputType, - Span span) { + Span span, + String documentation) { } record CallableParameter( @@ -282,7 +283,8 @@ final class PbsFlowSemanticSupport { method.returnKind(), method.returnType(), method.resultErrorType(), - method.span())); + method.span(), + documentationFromAttributes(method.attributes()))); } structs.put(structDecl.name(), new StructInfo(fields, methods)); return; @@ -306,7 +308,8 @@ final class PbsFlowSemanticSupport { signature.returnKind(), signature.returnType(), signature.resultErrorType(), - signature.span())); + signature.span(), + documentationFromAttributes(signature.attributes()))); } structs.put(builtinTypeDecl.name(), new StructInfo(fields, methods)); return; @@ -321,7 +324,8 @@ final class PbsFlowSemanticSupport { method.returnKind(), method.returnType(), method.resultErrorType(), - method.span())); + method.span(), + documentationFromAttributes(method.attributes()))); } services.put(serviceDecl.name(), new ServiceInfo(methods)); serviceSingletons.put(serviceDecl.name(), TypeView.service(serviceDecl.name())); @@ -337,7 +341,8 @@ final class PbsFlowSemanticSupport { signature.returnKind(), signature.returnType(), signature.resultErrorType(), - signature.span())); + signature.span(), + documentationFromAttributes(signature.attributes()))); } // Host owners are value singletons with callable members, same access shape as services. services.put(hostDecl.name(), new ServiceInfo(methods)); @@ -354,7 +359,8 @@ final class PbsFlowSemanticSupport { signature.returnKind(), signature.returnType(), signature.resultErrorType(), - signature.span())); + signature.span(), + documentationFromAttributes(signature.attributes()))); } contracts.put(contractDecl.name(), new ContractInfo(methods)); return; @@ -367,7 +373,8 @@ final class PbsFlowSemanticSupport { functionDecl.returnKind(), functionDecl.returnType(), functionDecl.resultErrorType(), - functionDecl.span())); + functionDecl.span(), + documentationFromAttributes(functionDecl.attributes()))); return; } if (topDecl instanceof PbsAst.CallbackDecl( @@ -381,7 +388,8 @@ final class PbsFlowSemanticSupport { returnKind, returnType, resultErrorType, - span); + span, + ""); callbacks.put(name, new CallbackSignature(symbol.inputTypes(), symbol.outputType())); return; } @@ -478,7 +486,8 @@ final class PbsFlowSemanticSupport { final PbsAst.ReturnKind returnKind, final PbsAst.TypeRef returnType, final PbsAst.TypeRef resultErrorType, - final Span span) { + final Span span, + final String documentation) { final var input = new ArrayList(parameters.size()); final var semanticParameters = new ArrayList(parameters.size()); for (int index = 0; index < parameters.size(); index += 1) { @@ -491,7 +500,21 @@ final class PbsFlowSemanticSupport { type, parameter.span())); } - return new CallableSymbol(name, semanticParameters, input, callableReturn(returnKind, returnType, resultErrorType), span); + return new CallableSymbol(name, semanticParameters, input, callableReturn(returnKind, returnType, resultErrorType), span, documentation); + } + + private String documentationFromAttributes(final ReadOnlyList attributes) { + for (final var attribute : attributes) { + if (!"Doc".equals(attribute.name()) || attribute.arguments().isEmpty()) { + continue; + } + final var argument = attribute.arguments().getFirst(); + if (!"markdown".equals(argument.name()) || !(argument.value() instanceof PbsAst.AttributeDocTextBlockValue docValue)) { + continue; + } + return p.studio.compiler.pbs.PbsDocumentationTextBlockNormalizer.normalizeLexeme(docValue.value()); + } + return ""; } private TypeView callableReturn( diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/semantics/PbsEditorialSupportServiceTest.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/semantics/PbsEditorialSupportServiceTest.java index 3821b9b9..8ba0a828 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/semantics/PbsEditorialSupportServiceTest.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/semantics/PbsEditorialSupportServiceTest.java @@ -123,6 +123,49 @@ final class PbsEditorialSupportServiceTest { assertEquals("Input", hostMethodHover.origin()); } + @Test + void shouldExposeDocMetadataOnTopLevelSymbolHover() { + final var source = """ + [Doc(markdown = \""" + Runs the program. + + - no parameters + \""")] + fn run() -> void { return; } + """; + final var ast = parseOrdinary(source); + + final var hover = requireHover(source, ast, ReadOnlyList.empty(), "run"); + + assertEquals(PbsEditorialSymbolKind.FUNCTION, hover.kind()); + assertEquals("Runs the program.\n\n- no parameters", hover.documentation()); + } + + @Test + void shouldExposeDocMetadataOnMemberHover() { + final var source = """ + import { Gfx } from @sdk:gfx; + + fn main() -> void { + Gfx.draw_pixel(1, 2, 3); + } + """; + final var ast = parseOrdinary(source); + final var supplementalTopDecls = supplementalTopDecls(""" + declare host Gfx { + [Doc(markdown = \""" + Draws a single pixel. + \""")] + fn draw_pixel(x: int, y: int, color: int) -> void; + } + """); + + final var hover = requireHover(source, ast, supplementalTopDecls, "draw_pixel"); + + assertEquals(PbsEditorialSymbolKind.METHOD, hover.kind()); + assertEquals("Draws a single pixel.", hover.documentation()); + } + @Test void shouldResolveSignatureHelpForFunctionMethodAndConstructorCalls() { final var source = """