Add PBS workspace symbols as a physical flatten of the outline named-declaration set on FrontendLanguageService. Regular files only; virtual stdlib, barrels, imports, locals, and compile-only frontends stay empty. Advertise workspaceSymbolProvider with flat SymbolInformation. workspace/symbol does not require an open document. Housekeep DSC-0042 with LSN-0071.
9.0 KiB
| id | ticket | title | created | tags | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| LSN-0071 | pbs-lsp-workspace-symbols | PBS workspace symbols are a physical named-declaration search | 2026-09-21 |
|
PBS workspace symbols are a physical named-declaration search
Original Problem
PBS already had compiler-backed completion, hover, signature help, go-to-definition (LSN-0068), find-references (LSN-0069), and an AST-backed outline of the open .pbs file (LSN-0070). VS Code workspace search (Ctrl+T / workspace/symbol) stayed empty.
The risk was treating workspace symbols as a second outline, inventing a SymbolId index, waiting for snapshot cache (AGD-0051), or returning stdlib/SDK hits whose declarations are still virtual paths (/virtual/stdlib). Workspace symbols is a jump. Outline is not.
Consolidated Decision
PBS Workspace Symbols is the LSP projection of a flat named-declaration search over regular filesystem files of the current analysis, flattening the same outline set and using the same physical-file destination policy as definition.
Durable locks from DEC-0055 (AGD-0045 Q1 A, Q2 A, Q3 A, Q4 A, Q5 A, Q6 first-wave exclusions):
- Emit only declarations whose
fileTablehandle is a regular filesystem file. Virtual stdlib/SDK, including/virtual/stdlib, is omitted. Do not invent virtual documents,untitled:buffers, or synthetic URIs. workspace/symboldoes not require an open text document. The server obtains the project analysis surface (astByFile/projectSurface) fromanalyze(). Missing surface → empty list.- The set is the flatten, file by file, of the named-declaration tree locked by outline (
LSN-0070):fn→ Function;declare struct→ Struct plus fields,ctors, methods;declare service→ Service plus methods;declare contract→ Contract plus signatures;declare host→ Host plus signatures;declare builtin type→ Builtin type plus projection fields and signatures;declare enum→ Enum plus cases;declare error→ Error plus labels;declare callback→ Callback;declare global/const→ Global / Const;implements Contract for Owner→contractName, kind CONTRACT,detail= owner, plus methods. Locals, parameters,this, imports, and comments stay out. Private fields stay in. Overloads stay sibling hits. UnnamedInvalidDeclis omitted. Named decls survive later semantic errors.
- First wave is
.pbswhose recovered root isPbsAst.File.mod.barrel(BarrelFile) stays empty. Presence, name, kind,detail, and spans come from recovered AST. Do not invent nodes from text search. - Query is a case-insensitive substring of
nameonly. Blank query returns the full first-wave corpus. Do not matchdetail, path,containerName, or qualifiedVec.blend. No fuzzy ranking, notop N. After the filter, order is stable by name, then path, then start offset. containerNameis the parent name for members and empty for top-level declarations.detailmay repeat the outline AST signature. Attributes such as[Init]do not enter the name.- Keep the capability on aggregated
FrontendLanguageService. AddworkspaceSymbols(default empty) plus a flat DTO withname,kind, optionaldetail,containerName, absolute path, and offsets. Do not reuse pathlessFrontendDocumentSymbol. PBS flattens inprometeu-frontend-pbs. Common LSP must not importp.studio.compiler.pbs.*. No Workspace Symbol SPI,SymbolId,RefIndex, or snapshot cache. - Advertise
workspaceSymbolProviderwhen the frontend exposes a language service. Payload is flatSymbolInformation. Do not emit hierarchicalDocumentSymbolon this request. Compile-only frontends stay empty. VS Code stays a thin client.
Spec 23 §8.3 now also states: workspace symbols are a flat search of named declarations in compiler-known physical source files of the current analysis; recovered syntax is enough; a missing analysis surface is empty; the request must not require an open document; a declaration without a physical file is omitted; this is not outline, folding, a persistent index, or navigation to virtual locations.
Final Implementation
| Layer | What landed |
|---|---|
| PBS editorial | PbsAstWorkspaceSymbols flattens PbsAstDocumentSymbols and filters by name. PbsEditorialSupportService.workspaceSymbols(ast, query) delegates to that flatten. |
| Generic contract | FrontendWorkspaceRequest (projectRoot, query). FrontendWorkspaceSymbol carries path. FrontendLanguageService.workspaceSymbols defaults to List.of(). prepareEditorialContext(FrontendPhaseContext) defaults to empty so a workspace request can obtain project surface without a FileId. |
| PBS mapping | Project-level prepareEditorialContext attaches fileTable + astByFile. Regular PbsAst.File entries are flattened; non-regular handles are skipped. Results sort by name, path, offset. |
| LSP | workspaceSymbolProvider, workspace/symbol on PrometeuWorkspaceService (no document URI). CompilerLanguageServiceBridge analyzes the project with an empty overlay map. Mapper emits flat SymbolInformation. Nested enum/error hits with a container project to EnumMember. Compile-only frontends stay empty. |
| VS Code | Unchanged thin client. |
workspace/symbol in this wave reads the project on disk. Unsaved editor overlays are not shared into the search. Empty is honest until a later overlay/snapshot decision.
mod.barrel is still BarrelFile, not PbsAst.File. Empty barrel hits are the honest first-wave result.
Error case labels still have no per-label span. Flattened children reuse the parent ErrorDecl span, same as outline.
Examples
Workspace search of a project file is the flatten of that file's outline, not imports or locals:
import { Log } from @sdk:log;
fn helper() -> int { return 42; }
fn frame() -> void {
let local = 1;
helper(local);
}
Query frame → frame. Query helper → helper. Query empty → helper, frame (plus other physical decls), sorted. Not Log, not local.
Members keep a container; overloads stay siblings:
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; }
Query blend → method blend with containerName Vec. Query helper → two hits. Query Vec.blend → empty (this wave matches name only).
Stdlib remains hover-only while the handle is virtual:
import { Gfx } from @sdk:gfx;
Gfx.clear(0);
Query Gfx is empty. F12 on Gfx.clear is already empty (LSN-0068). Workspace search does not reopen that jump.
Recovered parse keeps the named decl:
declare ;
fn ok() -> int { return 1; }
Query empty → ok. The unnamed InvalidDecl is omitted.
[Init] fn init() still names the symbol init.
Pitfalls
Do not implement workspace symbols as workspace text search in lsp-v1 or in the VS Code extension. It will lie about overloads, members, and imports.
Do not reuse FrontendDocumentSymbol. Outline is pathless because the host already has the document URI. Workspace symbols is a jump and must carry a path.
Do not require an open editor buffer to answer workspace/symbol. Analyze the project and walk astByFile.
Do not include /virtual/stdlib so Ctrl+T on Gfx “does something”. Empty is the honest result until those sources are regular files.
Do not wait for snapshot cache. References already walks the current analysis request-locally. Snapshots (AGD-0051) can make the walk cheaper without changing this contract.
Do not add a SymbolId index “for rename later”. Outline already named the declarations; definition already named the destination.
Do not match containerName, path, or Vec.blend in this wave. The query is a substring of name.
Do not emit hierarchical DocumentSymbol on workspace/symbol. That payload is outline.
Do not treat unsaved overlay absence as a missing feature of this contract. First-wave analysis is the project on disk.
Do not split a Workspace Symbol SPI “for future languages”. PBS is a real consumer of FrontendLanguageService (LSN-0067).
Do not treat this lesson as closing rename, snapshots, folding, document links, or hierarchy.
References
- Agenda:
AGD-0045(Q1 A, Q2 A, Q3 A, Q4 A, Q5 A, Q6 first-wave exclusions) - Decision:
DEC-0055 - Plan:
PLN-0134 - Spec 23 §8.3 — optional workspace symbols; physical-file-or-omit; no open document required
LSN-0070— outline set this feature flattensLSN-0068— physical-file destination this feature reusesLSN-0069— projectastByFilewalk already exists for referencesLSN-0058— genericFrontendLanguageServicefor LSPLSN-0047— protocol stays inlsp-v1; compiler owns semanticsLSN-0067— no SPI without a real in-repo consumer- Still open:
AGD-0046rename,AGD-0051snapshots,AGD-0053folding,AGD-0055document links,AGD-0056hierarchy