Some checks are pending
Intrepid/Prometeu/Studio/pipeline/pr-master Build started...
JaCoCo Coverage #### Project Overview
No changes detected, that affect the code coverage.
* Line Coverage: 62.84% (18504/29445)
* Branch Coverage: 53.33% (7215/13528)
* Lines of Code: 29445
* Cyclomatic Complexity: 11850
#### Quality Gates Summary
Output truncated.
Test / Build skipped: 15, passed: 669
Intrepid/Prometeu/Studio/pipeline/head This commit looks good
Add PBS document symbols as an AST-backed hierarchical outline of the open .pbs file on FrontendLanguageService. Recovered syntax is enough; unnamed InvalidDecl is omitted; barrels, imports, locals, and compile-only frontends stay empty. Advertise documentSymbolProvider with hierarchical DocumentSymbol. Housekeep DSC-0041 with LSN-0070.
142 lines
7.7 KiB
Markdown
142 lines
7.7 KiB
Markdown
---
|
|
id: LSN-0070
|
|
ticket: pbs-lsp-document-symbols-outline
|
|
title: PBS outline is an AST-backed document-symbol tree
|
|
created: 2026-09-21
|
|
tags: [studio, lsp, vscode, compiler-pbs, editor, outline, document-symbols]
|
|
---
|
|
|
|
# PBS outline is an AST-backed document-symbol tree
|
|
|
|
## Original Problem
|
|
|
|
PBS already had compiler-backed completion, hover, signature help, go-to-definition (`LSN-0068`), and find-references (`LSN-0069`). VS Code Outline stayed empty. Long `.pbs` files had to be scanned by hand.
|
|
|
|
The risk was building outline from semantic success (so the tree vanishes while typing), flattening to `SymbolInformation`, inventing a Document Symbol SPI, or stuffing workspace search and folding into the same payload.
|
|
|
|
## Consolidated Decision
|
|
|
|
PBS Document Symbols is the LSP projection of the **named declaration tree of the open `.pbs` file**, with the **recovered AST as backbone**.
|
|
|
|
Durable locks from `DEC-0054` (AGD-0044 Q1 A, Q2 AST tree, Q3 A, Q4 A, Q5 A, Q6 `.pbs`-only, Q7 existing kinds):
|
|
|
|
1. Presence, hierarchy, and ranges come from the document AST. Kind and `detail` may use cheap semantic enrichment. Semantics must not drop a named declaration still in the syntax tree, and must not require full analysis.
|
|
2. Named `TopDecl` plus syntactic members:
|
|
- `fn` → Function, no children;
|
|
- `declare struct` → Struct, children: fields, `ctor`s, methods;
|
|
- `declare service` → Service, children: methods;
|
|
- `declare contract` → Contract, children: signatures as methods;
|
|
- `declare host` → Host, children: signatures;
|
|
- `declare builtin type` → Builtin type, children: projection fields and signatures;
|
|
- `declare enum` → Enum, children: cases;
|
|
- `declare error` → Error, children: labels;
|
|
- `declare callback` → Callback, no children;
|
|
- `declare global` / `const` → Global / Const, no children;
|
|
- `implements Contract for Owner` → top-level Contract named `contractName`, `detail` = owner name, children: methods.
|
|
Locals, parameters, `this`, imports, and comments stay out. Private fields stay in. Overloads stay siblings. Host and builtin type appear only if the open file declares them.
|
|
3. Recovered AST is enough. Unnamed `InvalidDecl` is omitted. Named decls survive later semantic errors. No `PbsAst.File` → empty list. Do not invent nodes from text or token pairs.
|
|
4. Keep the capability on aggregated `FrontendLanguageService.documentSymbols` (default empty) plus a hierarchical DTO (`name`, `kind`, optional `detail`, range offsets, selection offsets, `children`). No file path on the DTO. PBS builds the tree in `prometeu-frontend-pbs`. Common LSP must not import `p.studio.compiler.pbs.*`. No Document Symbol SPI.
|
|
5. Advertise `documentSymbolProvider`. Payload is hierarchical `DocumentSymbol`, not flat `SymbolInformation`. `range` is the declaration span. `selectionRange` is the name span only if the AST already has one; this wave reuses the declaration span. Do not scan tokens for identifier spans. VS Code stays a thin client.
|
|
6. First wave is the current `.pbs` only. Out: imported supplementals, `mod.barrel`, locals, parameters, imports, comments, virtual documents, workspace symbols, folding, rename, snapshot cache. Server order is AST declaration order, not alphabetical.
|
|
7. Reuse `FrontendSymbolKind` / `PbsEditorialSymbolKind`. LSP projects to the closest host `SymbolKind`. Name is the identifier. `detail` may carry the AST signature (`(a: int) -> int`). Attributes such as `[Init]` do not enter the name.
|
|
|
|
Outline does not jump to another file. The physical-file destination policy of definition/references is not the outline policy.
|
|
|
|
Spec 23 §8.3 now also states: document symbols are the named declaration tree of the requested document; recovered syntax is enough; a missing syntax tree is empty; this is not a workspace index, folding ranges, or a foreign-file navigation list.
|
|
|
|
## Final Implementation
|
|
|
|
| Layer | What landed |
|
|
|---|---|
|
|
| PBS editorial | `PbsAstDocumentSymbols` walks `PbsAst.File.topDecls` in source order. `PbsEditorialSupportService.documentSymbols` delegates to that walk. |
|
|
| Generic contract | `FrontendLanguageService.documentSymbols` defaults to `List.of()`. `FrontendDocumentSymbol` is hierarchical and pathless. |
|
|
| PBS mapping | `PBSFrontendLanguageService` requires `editorialContext.syntaxTree()` to be `PbsAst.File`. Barrels and unknown trees stay empty. Kinds map with `valueOf(kind.name())`. |
|
|
| LSP | `documentSymbolProvider`, `textDocument/documentSymbol`, hierarchical `DocumentSymbol`. Nested enum/error children project to `EnumMember`. Compile-only frontends stay empty. |
|
|
| VS Code | Unchanged thin client. |
|
|
|
|
`mod.barrel` is assembled as `BarrelFile`, not `PbsAst.File`, so `astByFile` has no barrel outline. Empty is the honest first-wave result.
|
|
|
|
Error case labels have no per-label span in the AST. Children reuse the parent `ErrorDecl` span rather than scanning tokens.
|
|
|
|
## Examples
|
|
|
|
Outline of a file is the declarations in that file, not imports or locals:
|
|
|
|
```pbs
|
|
import { Log } from @sdk:log;
|
|
|
|
fn helper() -> int { return 42; }
|
|
|
|
fn frame() -> void {
|
|
let local = 1;
|
|
helper(local);
|
|
}
|
|
```
|
|
|
|
Outline: `helper`, `frame`. Not `Log`, not `local`.
|
|
|
|
Struct members and overloads as siblings:
|
|
|
|
```pbs
|
|
declare struct Vec(x: int) {
|
|
ctor make(x: int) { return; }
|
|
fn blend(dx: int, dy: int) -> int { return dx; }
|
|
}
|
|
|
|
fn helper() -> int { return 42; }
|
|
fn helper(value: int) -> int { return value; }
|
|
```
|
|
|
|
Outline: `Vec` (`x`, `make`, `blend`), then two `helper` siblings. Private field `x` is present. `detail` on `blend` is `(dx: int, dy: int) -> int`.
|
|
|
|
Recovered parse keeps the named decl:
|
|
|
|
```pbs
|
|
declare ;
|
|
fn ok() -> int { return 1; }
|
|
```
|
|
|
|
Outline: `ok`. The unnamed `InvalidDecl` is omitted.
|
|
|
|
`[Init] fn init()` still names the symbol `init`.
|
|
|
|
`implements TickLike for Point` is a top-level node named `TickLike` with detail `Point`.
|
|
|
|
## Pitfalls
|
|
|
|
Do not wait for semantic analysis to succeed before showing outline. The tree must survive broken intermediate edits.
|
|
|
|
Do not omit private fields because they are not in `mod.barrel`. Outline is for the author of this file.
|
|
|
|
Do not collapse overloads. AST identity forbids merging declaration sets.
|
|
|
|
Do not walk `File.imports` or supplemental imported `TopDecl`s. Those symbols are not declared in this document.
|
|
|
|
Do not implement outline for `mod.barrel` in this contract. Barrel AST is a different root. Empty is correct until a later decision.
|
|
|
|
Do not scan tokens to invent a tighter `selectionRange`. This wave uses the declaration span.
|
|
|
|
Do not put a file path on `FrontendDocumentSymbol`. The host already has the document URI. Outline is not go-to-definition.
|
|
|
|
Do not reuse the physical-file jump rule of `LSN-0068` as an outline destination policy. Clicking outline stays inside the buffer.
|
|
|
|
Do not overload document symbols with folding or scope guides (`LSN-0034`, `AGD-0053`).
|
|
|
|
Do not split a Document Symbol SPI “for future languages”. PBS is a real consumer of `FrontendLanguageService` (`LSN-0067`).
|
|
|
|
Do not treat this lesson as closing workspace symbols, folding, rename, document links, or hierarchy.
|
|
|
|
## References
|
|
|
|
- Agenda: `AGD-0044` (Q1 A, Q2 AST tree, Q3 A, Q4 A, Q5 A, Q6 `.pbs`-only, Q7 existing kinds)
|
|
- Decision: `DEC-0054`
|
|
- Plan: `PLN-0133`
|
|
- Spec 23 §8.3 — optional document symbols; recovered AST tree of the requested document
|
|
- `LSN-0068` — definition is a jump; outline is not
|
|
- `LSN-0069` — references invert definition identity
|
|
- `LSN-0034` — do not overload document symbols with structural anchors
|
|
- `LSN-0058` — generic `FrontendLanguageService` for LSP
|
|
- `LSN-0047` — protocol stays in `lsp-v1`; compiler owns semantics
|
|
- `LSN-0067` — no SPI without a real in-repo consumer
|
|
- Still open: `AGD-0045` workspace symbols, `AGD-0046` rename, `AGD-0051` snapshots, `AGD-0053` folding, `AGD-0055` document links, `AGD-0056` hierarchy
|