Compare commits

..

38 Commits

Author SHA1 Message Date
189f4f2073 Merge pull request 'dev/prometeu-lsp' (#8) from dev/prometeu-lsp into master
All checks were successful
JaCoCo Coverage #### Project Overview No changes detected, that affect the code coverage. * Line Coverage: 61.88% (17291/27941) * Branch Coverage: 52.91% (6692/12647) * Lines of Code: 27941 * Cyclomatic Complexity: 11191 #### Quality Gates Summary Output truncated.
Test / Build skipped: 11, passed: 590
Intrepid/Prometeu/Studio/pipeline/head This commit looks good
Reviewed-on: #8
2026-05-08 09:54:16 +00:00
2f289b9425
update gitignore
All checks were successful
JaCoCo Coverage #### Project Overview No changes detected, that affect the code coverage. * Line Coverage: 61.88% (17291/27941) * Branch Coverage: 52.91% (6692/12647) * Lines of Code: 27941 * Cyclomatic Complexity: 11191 #### Quality Gates Summary Output truncated.
Test / Build skipped: 11, passed: 577
Intrepid/Prometeu/Studio/pipeline/head This commit looks good
Intrepid/Prometeu/Studio/pipeline/pr-master This commit looks good
2026-05-08 10:30:54 +01:00
54339a95b8
implements PLN-0077 2026-05-08 10:15:17 +01:00
7cbe61f5eb
implements PLN-0076 2026-05-08 10:13:38 +01:00
7f40541e96
implements PLN-0075 2026-05-08 10:04:39 +01:00
240525286b
Wave 1 de assistencia editorial via LSP para PBS no VS Code 2026-05-08 09:48:14 +01:00
ef814b2420
housekeeping 2026-05-08 09:17:43 +01:00
0e6a2cd5bc
housekeeping 2026-05-07 14:53:33 +01:00
905d92afe7
Highlight color adjustments 2026-05-07 14:21:31 +01:00
dfad621d05
Frontend Visual Theme Spec and Retirement of Host-Consumed Semantic CSS 2026-05-06 17:20:38 +01:00
e23367b022
Frontend Visual Theme Spec and Retirement of Host-Consumed Semantic CSS 2026-05-06 15:50:35 +01:00
10fd7f6111
Frontend Visual Theme Spec and Retirement of Host-Consumed Semantic CSS 2026-05-06 15:46:46 +01:00
fb13e166f1
Frontend Visual Theme Spec and Retirement of Host-Consumed Semantic CSS 2026-05-06 15:39:43 +01:00
7ae2e94d63
implements PLN-0074 2026-05-06 15:28:37 +01:00
e5ab4cd021
implements PLN-0073 2026-05-06 15:26:24 +01:00
9d75f2f0e2
implements PLN-0072 2026-05-06 15:24:21 +01:00
cab0e92e60
Frontend Visual Theme Spec and Retirement of Host-Consumed Semantic CSS 2026-05-06 15:21:15 +01:00
69f5c4abdd
implements PLN-0071 2026-05-06 13:53:23 +01:00
ed2070c5a4
implements PLN-0070 2026-05-06 13:49:58 +01:00
41bcd8c52a
implements PLN-0069 2026-05-06 13:48:09 +01:00
54f8c0d6d8
implements PLN-0068 2026-05-06 13:46:23 +01:00
baf9f90c93
Frontend Visual Theme Spec and Retirement of Host-Consumed Semantic CSS 2026-05-06 13:43:28 +01:00
0cafe84da0
1st semantic version deployed 2026-05-06 09:41:36 +01:00
2994bc67a3
refactor and clean up 2026-05-06 04:15:36 +01:00
0ac7d9e732
adjusts on client-server connection (LSP) 2026-05-06 04:06:09 +01:00
85a224fc7b
basic for analyse 2026-05-06 03:40:55 +01:00
41f2c804a0
clean up and simplify 2026-05-06 02:58:18 +01:00
14d4751f9a
package moving 2026-05-06 00:25:23 +01:00
7415b70d1b
package moving 2026-05-06 00:21:41 +01:00
ba88dee8cb
connection checker and failure 2026-05-05 16:23:57 +01:00
f6774ef568
implements PLN-0067 2026-05-05 15:57:27 +01:00
18dc64e36c
implements PLN-0066 2026-05-05 15:54:11 +01:00
b0073cd7bd
implements PLN-0065 2026-05-05 15:50:55 +01:00
e5342e34f7
added simple README.md on vscode-extension 2026-05-05 13:42:05 +01:00
52211ea02c
add bare bones for a lsp 2026-05-05 13:39:24 +01:00
6b60fa9d33
implements PLN-0064 2026-05-05 11:20:09 +01:00
1f85be3721
implements PLN-0063 2026-05-05 11:14:45 +01:00
cebef90118
implements PLN-0062 2026-05-05 11:12:37 +01:00
217 changed files with 8808 additions and 9739 deletions

View File

@ -2,7 +2,7 @@
Prometeu Studio is the desktop development environment for the Prometeu toolchain.
This repository packages the Studio shell together with the embedded services it depends on: project VFS, language tooling, compiler pipeline integration, and asset packing. The current implementation is a Java/JavaFX multi-module Gradle build.
This repository packages the Studio shell together with the embedded services it depends on: compiler pipeline integration, asset packing, runtime/debug orchestration, and project-local Studio state. The current implementation is a Java/JavaFX multi-module Gradle build.
## What Lives Here
@ -10,8 +10,6 @@ The root project is a monorepo with these main areas:
- `prometeu-app`: JavaFX application entry point (`p.studio.App`).
- `prometeu-studio`: Studio UI, workspaces, project session lifecycle, play/stop flow, and debugger-facing surfaces.
- `prometeu-vfs`: project document access and save/open contracts.
- `prometeu-lsp`: language-service integration used by the editor workflow.
- `prometeu-packer`: embedded packer APIs and implementation used by Studio.
- `prometeu-compiler`: compiler core, build pipeline, frontend APIs, and the PBS frontend.
- `prometeu-infra`: shared infrastructure used across modules.
@ -21,14 +19,13 @@ The root project is a monorepo with these main areas:
## Current Product Shape
The Studio shell currently organizes the product around four workspaces:
The Studio shell currently organizes the product around three workspaces:
- Assets
- Code editor
- Debug
- Shipper
At runtime, the app boots an embedded packer, creates a project document VFS, wires LSP services, and opens the Studio launcher window. Project creation currently scaffolds a Prometeu workspace with `prometeu.json`, `src/`, `assets/`, `build/`, `cartridge/`, `.workspace/`, and local Studio metadata.
At runtime, the app boots an embedded packer, restores project-local Studio state, and opens the Studio launcher window. Project creation currently scaffolds a Prometeu workspace with `prometeu.json`, `src/`, `assets/`, `build/`, `cartridge/`, `.workspace/`, and local Studio metadata.
## Requirements
@ -68,7 +65,7 @@ If you are new to the codebase, this order is usually the fastest path:
1. Start at `prometeu-app` to see application bootstrap.
2. Read `prometeu-studio` for the shell, workspaces, and user-facing flows.
3. Read `prometeu-vfs`, `prometeu-lsp`, and `prometeu-packer` for service boundaries.
3. Read `prometeu-packer` for asset and packaging boundaries.
4. Read `prometeu-compiler` when you need compiler or PBS language behavior.
## Specs And Architecture

View File

@ -1,12 +0,0 @@
{"type":"meta","next_id":{"DSC":12,"AGD":12,"DEC":9,"PLN":15,"LSN":26,"CLSN":1}}
{"type":"discussion","id":"DSC-0001","status":"done","ticket":"studio-docs-import","title":"Import docs/studio into discussion-framework artifacts","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["studio","migration","discussion-framework","docs-import"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0001","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0001-assets-workspace-execution-wave-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0002","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0002-bank-composition-editor-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0003","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0003-mental-model-asset-mutations-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0004","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0004-mental-model-assets-workspace-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0005","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0005-mental-model-studio-events-and-components-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0006","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0006-mental-model-studio-shell-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0007","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0007-pack-wizard-shell-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0008","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0008-project-scoped-state-and-activity-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0016","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0016-studio-docs-import-pattern.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"}]}
{"type":"discussion","id":"DSC-0002","status":"open","ticket":"palette-management-in-studio","title":"Palette Management in Studio","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["studio","legacy-import","palette-management","tile-bank","packer-boundary"],"agendas":[{"id":"AGD-0002","file":"AGD-0002-palette-management-in-studio.md","status":"open","created_at":"2026-03-26","updated_at":"2026-03-26"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0003","status":"done","ticket":"packer-docs-import","title":"Import docs/packer into discussion-framework artifacts","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["packer","migration","discussion-framework","docs-import"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0009","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0009-mental-model-packer-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0010","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0010-asset-identity-and-runtime-contract-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0011","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0011-foundations-workspace-runtime-and-build-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0012","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0012-runtime-ownership-and-studio-boundary-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0013","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0013-metadata-convergence-and-runtime-sink-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0014","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0014-pack-wizard-summary-validation-and-pack-execution-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0015","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0015-tile-bank-packing-contract-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0017","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0017-packer-docs-import-pattern.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"}]}
{"type":"discussion","id":"DSC-0004","status":"open","ticket":"tilemap-and-metatile-runtime-binary-layout","title":"Tilemap and Metatile Runtime Binary Layout","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["packer","legacy-import","tilemap","metatile","runtime-layout"],"agendas":[{"id":"AGD-0004","file":"AGD-0004-tilemap-and-metatile-runtime-binary-layout.md","status":"open","created_at":"2026-03-26","updated_at":"2026-03-26"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0005","status":"open","ticket":"variable-tile-bank-palette-serialization","title":"Variable Tile Bank Palette Serialization","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["packer","legacy-import","tile-bank","palette-serialization","versioning"],"agendas":[{"id":"AGD-0005","file":"AGD-0005-variable-tile-bank-palette-serialization.md","status":"open","created_at":"2026-03-26","updated_at":"2026-03-26"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0006","status":"done","ticket":"pbs-game-facing-asset-refs-and-call-result-discard","title":"PBS Game-Facing Asset References and Ignored Call Result Lowering","created_at":"2026-03-27","updated_at":"2026-03-30","tags":["compiler","pbs","ergonomics","lowering","runtime","asset-identity","expression-statements"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0024","file":"discussion/lessons/DSC-0006-pbs-game-facing-asset-refs-and-call-result-discard/LSN-0024-addressable-surface-host-metadata-and-ignored-value-discipline.md","status":"done","created_at":"2026-03-30","updated_at":"2026-03-30"}]}
{"type":"discussion","id":"DSC-0007","status":"done","ticket":"pbs-learn-to-discussion-lessons-migration","title":"Migrate PBS Learn Documents into Discussion Lessons","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["compiler","pbs","migration","discussion-framework","lessons","learn-prune"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0018","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0018-pbs-ast-and-parser-contract-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0019","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0019-pbs-name-resolution-and-linking-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0020","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0020-pbs-runtime-values-identity-memory-boundaries-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0021","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0021-pbs-diagnostics-and-conformance-governance-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0022","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0022-pbs-globals-lifecycle-and-published-entrypoint-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]}
{"type":"discussion","id":"DSC-0008","status":"done","ticket":"pbs-low-level-asset-manager-surface","title":"PBS Low-Level Asset Manager Surface for Runtime AssetManager","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["compiler","pbs","runtime","asset-manager","host-abi","stdlib","asset"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0023","file":"discussion/lessons/DSC-0008-pbs-low-level-asset-manager-surface/LSN-0023-lowassets-runtime-aligned-sdk-surface.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]}
{"type":"discussion","id":"DSC-0009","status":"open","ticket":"studio-debugger-workspace-integration","title":"Integrate ../debugger into Studio as a dedicated workspace","created_at":"2026-03-30","updated_at":"2026-03-30","tags":["studio","debugger","workspace","integration","shell"],"agendas":[{"id":"AGD-0009","file":"AGD-0009-studio-debugger-workspace-integration.md","status":"open","created_at":"2026-03-30","updated_at":"2026-03-30"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0010","status":"in_progress","ticket":"studio-code-editor-workspace-foundations","title":"Establish Code Editor workspace foundations in Studio without LSP","created_at":"2026-03-30","updated_at":"2026-03-30","tags":["studio","editor","workspace","multi-frontend","lsp-deferred"],"agendas":[{"id":"AGD-0010","file":"AGD-0010-studio-code-editor-workspace-foundations.md","status":"accepted","created_at":"2026-03-30","updated_at":"2026-03-30"}],"decisions":[{"id":"DEC-0008","file":"DEC-0008-studio-code-editor-read-only-workspace-foundations.md","status":"accepted","created_at":"2026-03-30","updated_at":"2026-03-30","ref_agenda":"AGD-0010"}],"plans":[{"id":"PLN-0012","file":"PLN-0012-studio-code-editor-spec-propagation.md","status":"done","created_at":"2026-03-30","updated_at":"2026-03-30","ref_decisions":["DEC-0008"]},{"id":"PLN-0013","file":"PLN-0013-editor-workspace-layout-and-passive-surfaces.md","status":"done","created_at":"2026-03-30","updated_at":"2026-03-30","ref_decisions":["DEC-0008"]},{"id":"PLN-0014","file":"PLN-0014-project-navigator-snapshot-and-read-only-file-opening.md","status":"done","created_at":"2026-03-30","updated_at":"2026-03-30","ref_decisions":["DEC-0008"]}],"lessons":[]}
{"type":"discussion","id":"DSC-0011","status":"done","ticket":"compiler-analyze-compile-build-pipeline-split","title":"Split compiler pipeline into analyze, compile, and build entrypoints","created_at":"2026-03-30","updated_at":"2026-03-30","tags":["compiler","pipeline","artifacts","build","analysis"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0025","file":"discussion/lessons/DSC-0011-compiler-analyze-compile-build-pipeline-split/LSN-0025-compiler-pipeline-entrypoints-and-result-boundaries.md","status":"done","created_at":"2026-03-30","updated_at":"2026-03-30"}]}

View File

@ -1,13 +0,0 @@
{"type":"meta","next_id":{"DSC":13,"AGD":13,"DEC":10,"PLN":19,"LSN":27,"CLSN":1}}
{"type":"discussion","id":"DSC-0001","status":"done","ticket":"studio-docs-import","title":"Import docs/studio into discussion-framework artifacts","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["studio","migration","discussion-framework","docs-import"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0001","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0001-assets-workspace-execution-wave-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0002","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0002-bank-composition-editor-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0003","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0003-mental-model-asset-mutations-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0004","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0004-mental-model-assets-workspace-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0005","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0005-mental-model-studio-events-and-components-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0006","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0006-mental-model-studio-shell-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0007","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0007-pack-wizard-shell-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0008","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0008-project-scoped-state-and-activity-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0016","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0016-studio-docs-import-pattern.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"}]}
{"type":"discussion","id":"DSC-0002","status":"open","ticket":"palette-management-in-studio","title":"Palette Management in Studio","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["studio","legacy-import","palette-management","tile-bank","packer-boundary"],"agendas":[{"id":"AGD-0002","file":"AGD-0002-palette-management-in-studio.md","status":"open","created_at":"2026-03-26","updated_at":"2026-03-26"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0003","status":"done","ticket":"packer-docs-import","title":"Import docs/packer into discussion-framework artifacts","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["packer","migration","discussion-framework","docs-import"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0009","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0009-mental-model-packer-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0010","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0010-asset-identity-and-runtime-contract-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0011","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0011-foundations-workspace-runtime-and-build-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0012","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0012-runtime-ownership-and-studio-boundary-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0013","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0013-metadata-convergence-and-runtime-sink-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0014","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0014-pack-wizard-summary-validation-and-pack-execution-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0015","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0015-tile-bank-packing-contract-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0017","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0017-packer-docs-import-pattern.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"}]}
{"type":"discussion","id":"DSC-0004","status":"open","ticket":"tilemap-and-metatile-runtime-binary-layout","title":"Tilemap and Metatile Runtime Binary Layout","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["packer","legacy-import","tilemap","metatile","runtime-layout"],"agendas":[{"id":"AGD-0004","file":"AGD-0004-tilemap-and-metatile-runtime-binary-layout.md","status":"open","created_at":"2026-03-26","updated_at":"2026-03-26"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0005","status":"open","ticket":"variable-tile-bank-palette-serialization","title":"Variable Tile Bank Palette Serialization","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["packer","legacy-import","tile-bank","palette-serialization","versioning"],"agendas":[{"id":"AGD-0005","file":"AGD-0005-variable-tile-bank-palette-serialization.md","status":"open","created_at":"2026-03-26","updated_at":"2026-03-26"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0006","status":"done","ticket":"pbs-game-facing-asset-refs-and-call-result-discard","title":"PBS Game-Facing Asset References and Ignored Call Result Lowering","created_at":"2026-03-27","updated_at":"2026-03-30","tags":["compiler","pbs","ergonomics","lowering","runtime","asset-identity","expression-statements"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0024","file":"discussion/lessons/DSC-0006-pbs-game-facing-asset-refs-and-call-result-discard/LSN-0024-addressable-surface-host-metadata-and-ignored-value-discipline.md","status":"done","created_at":"2026-03-30","updated_at":"2026-03-30"}]}
{"type":"discussion","id":"DSC-0007","status":"done","ticket":"pbs-learn-to-discussion-lessons-migration","title":"Migrate PBS Learn Documents into Discussion Lessons","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["compiler","pbs","migration","discussion-framework","lessons","learn-prune"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0018","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0018-pbs-ast-and-parser-contract-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0019","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0019-pbs-name-resolution-and-linking-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0020","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0020-pbs-runtime-values-identity-memory-boundaries-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0021","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0021-pbs-diagnostics-and-conformance-governance-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0022","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0022-pbs-globals-lifecycle-and-published-entrypoint-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]}
{"type":"discussion","id":"DSC-0008","status":"done","ticket":"pbs-low-level-asset-manager-surface","title":"PBS Low-Level Asset Manager Surface for Runtime AssetManager","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["compiler","pbs","runtime","asset-manager","host-abi","stdlib","asset"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0023","file":"discussion/lessons/DSC-0008-pbs-low-level-asset-manager-surface/LSN-0023-lowassets-runtime-aligned-sdk-surface.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]}
{"type":"discussion","id":"DSC-0009","status":"open","ticket":"studio-debugger-workspace-integration","title":"Integrate ../debugger into Studio as a dedicated workspace","created_at":"2026-03-30","updated_at":"2026-03-30","tags":["studio","debugger","workspace","integration","shell"],"agendas":[{"id":"AGD-0009","file":"AGD-0009-studio-debugger-workspace-integration.md","status":"open","created_at":"2026-03-30","updated_at":"2026-03-30"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0010","status":"done","ticket":"studio-code-editor-workspace-foundations","title":"Establish Code Editor workspace foundations in Studio without LSP","created_at":"2026-03-30","updated_at":"2026-03-31","tags":["studio","editor","workspace","multi-frontend","lsp-deferred"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0026","file":"discussion/lessons/DSC-0010-studio-code-editor-workspace-foundations/LSN-0026-read-only-editor-foundations-and-semantic-deferral.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31"}]}
{"type":"discussion","id":"DSC-0011","status":"done","ticket":"compiler-analyze-compile-build-pipeline-split","title":"Split compiler pipeline into analyze, compile, and build entrypoints","created_at":"2026-03-30","updated_at":"2026-03-30","tags":["compiler","pipeline","artifacts","build","analysis"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0025","file":"discussion/lessons/DSC-0011-compiler-analyze-compile-build-pipeline-split/LSN-0025-compiler-pipeline-entrypoints-and-result-boundaries.md","status":"done","created_at":"2026-03-30","updated_at":"2026-03-30"}]}
{"type":"discussion","id":"DSC-0012","status":"open","ticket":"studio-editor-document-vfs-boundary","title":"Definir um boundary de VFS documental para tree/view/open files no Code Editor do Studio","created_at":"2026-03-31","updated_at":"2026-03-31","tags":["studio","editor","workspace","vfs","filesystem","boundary"],"agendas":[{"id":"AGD-0012","file":"AGD-0012-studio-editor-document-vfs-boundary.md","status":"in_progress","created_at":"2026-03-31","updated_at":"2026-03-31"}],"decisions":[{"id":"DEC-0009","file":"DEC-0009-studio-prometeu-vfs-project-document-boundary.md","status":"in_progress","created_at":"2026-03-31","updated_at":"2026-03-31","ref_agenda":"AGD-0012"}],"plans":[{"id":"PLN-0015","file":"PLN-0015-propagate-dec-0009-into-studio-specs.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0009"]},{"id":"PLN-0016","file":"PLN-0016-build-prometeu-vfs-filesystem-backed-core.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0009"]},{"id":"PLN-0017","file":"PLN-0017-add-studio-project-session-ownership-for-prometeu-vfs.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0009"]},{"id":"PLN-0018","file":"PLN-0018-migrate-code-editor-to-prometeu-vfs.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0009"]}],"lessons":[]}

View File

@ -0,0 +1,35 @@
{"type":"meta","next_id":{"DSC":35,"AGD":38,"DEC":35,"PLN":75,"LSN":47,"CLSN":1}}
{"type":"discussion","id":"DSC-0034","status":"in_progress","ticket":"frontend-semantic-host-projection-flexibility","title":"Frontend semantic vocabulary flexibility and declarative host projection","created_at":"2026-05-06","updated_at":"2026-05-06","tags":["compiler","compiler-general","frontend","semantics","vscode","host-projection","lsp"],"agendas":[{"id":"AGD-0037","file":"AGD-0037-frontend-semantic-host-projection-flexibility.md","status":"accepted","created_at":"2026-05-06","updated_at":"2026-05-06"}],"decisions":[{"id":"DEC-0034","file":"DEC-0034-frontend-semantic-host-projection-flexibility.md","status":"accepted","created_at":"2026-05-06","updated_at":"2026-05-06","ref_agenda":"AGD-0037"}],"plans":[{"id":"PLN-0072","file":"PLN-0072-frontend-semantic-host-projection-contract.md","status":"done","created_at":"2026-05-06","updated_at":"2026-05-06","ref_decisions":["DEC-0034"]},{"id":"PLN-0073","file":"PLN-0073-lsp-semantic-host-projection-transport.md","status":"done","created_at":"2026-05-06","updated_at":"2026-05-06","ref_decisions":["DEC-0034"]},{"id":"PLN-0074","file":"PLN-0074-vscode-semantic-host-projection-adapter.md","status":"done","created_at":"2026-05-06","updated_at":"2026-05-06","ref_decisions":["DEC-0034"]}],"lessons":[]}
{"type":"discussion","id":"DSC-0033","status":"in_progress","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-06","tags":["compiler","compiler-general","frontend","presentation","theming","studio","vscode","lsp","pbs"],"agendas":[{"id":"AGD-0036","file":"AGD-0036-frontend-visual-theme-spec-and-css-retirement.md","status":"accepted","created_at":"2026-05-06","updated_at":"2026-05-06"}],"decisions":[{"id":"DEC-0033","file":"DEC-0033-frontend-visual-theme-spec-and-css-retirement.md","status":"accepted","created_at":"2026-05-06","updated_at":"2026-05-06","ref_agenda":"AGD-0036"}],"plans":[{"id":"PLN-0068","file":"PLN-0068-frontend-visual-contract-and-pbs-migration.md","status":"open","created_at":"2026-05-06","updated_at":"2026-05-06","ref_decisions":["DEC-0033"]},{"id":"PLN-0069","file":"PLN-0069-lsp-visual-theme-transport-and-selection.md","status":"open","created_at":"2026-05-06","updated_at":"2026-05-06","ref_decisions":["DEC-0033"]},{"id":"PLN-0070","file":"PLN-0070-studio-visual-theme-adapter-and-css-retirement.md","status":"open","created_at":"2026-05-06","updated_at":"2026-05-06","ref_decisions":["DEC-0033"]},{"id":"PLN-0071","file":"PLN-0071-vscode-dynamic-theme-translation-adapter.md","status":"open","created_at":"2026-05-06","updated_at":"2026-05-06","ref_decisions":["DEC-0033"]}],"lessons":[]}
{"type":"discussion","id":"DSC-0032","status":"in_progress","ticket":"studio-new-lsp-api-and-v1-boundary","title":"Novo boundary entre lsp-api, lsp-v1 e a extensao VS Code","created_at":"2026-05-05","updated_at":"2026-05-05","tags":["studio","lsp","vscode","protocol","api","boundary"],"agendas":[{"id":"AGD-0035","file":"AGD-0035-studio-new-lsp-api-and-v1-boundary.md","status":"accepted","created_at":"2026-05-05","updated_at":"2026-05-05"}],"decisions":[{"id":"DEC-0032","file":"DEC-0032-studio-new-lsp-api-and-v1-boundary.md","status":"accepted","created_at":"2026-05-05","updated_at":"2026-05-05","ref_agenda":"AGD-0035"}],"plans":[{"id":"PLN-0065","file":"PLN-0065-lsp-boundary-and-module-scaffolding.md","status":"done","created_at":"2026-05-05","updated_at":"2026-05-05","ref_decisions":["DEC-0032"]},{"id":"PLN-0066","file":"PLN-0066-project-scoped-lsp-server-lifecycle-in-studio.md","status":"done","created_at":"2026-05-05","updated_at":"2026-05-05","ref_decisions":["DEC-0032"]},{"id":"PLN-0067","file":"PLN-0067-compiler-backed-dumb-lsp-server-baseline.md","status":"done","created_at":"2026-05-05","updated_at":"2026-05-05","ref_decisions":["DEC-0032"]}],"lessons":[]}
{"type":"discussion","id":"DSC-0031","status":"in_progress","ticket":"studio-editor-workspace-cleanup","title":"Limpeza completa do Workspace Editor do Studio e remoção dos acoplamentos legados","created_at":"2026-05-05","updated_at":"2026-05-05","tags":["studio","editor","cleanup","vfs","lsp","migration"],"agendas":[{"id":"AGD-0034","file":"AGD-0034-vscode-editor-migration-feasibility.md","status":"accepted","created_at":"2026-05-05","updated_at":"2026-05-05"}],"decisions":[{"id":"DEC-0031","file":"DEC-0031-studio-editor-workspace-cleanup.md","status":"in_progress","created_at":"2026-05-05","updated_at":"2026-05-05","ref_agenda":"AGD-0034"}],"plans":[{"id":"PLN-0062","file":"PLN-0062-shell-session-and-state-cleanup-after-editor-removal.md","status":"done","created_at":"2026-05-05","updated_at":"2026-05-05","ref_decisions":["DEC-0031"]},{"id":"PLN-0063","file":"PLN-0063-legacy-lsp-and-vfs-module-removal-with-runtime-preservation.md","status":"done","created_at":"2026-05-05","updated_at":"2026-05-05","ref_decisions":["DEC-0031"]},{"id":"PLN-0064","file":"PLN-0064-normative-test-and-lesson-cleanup-for-editor-stack-removal.md","status":"done","created_at":"2026-05-05","updated_at":"2026-05-05","ref_decisions":["DEC-0031"]}],"lessons":[]}
{"type":"discussion","id":"DSC-0030","status":"done","ticket":"studio-scene-pack-runtime-binary-contract","title":"Studio scene pack contract for runtime SCENE binary payload","created_at":"2026-04-24","updated_at":"2026-05-01","tags":["studio","packer","runtime","scene","asset-pack","binary-format","tiled"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0046","file":"discussion/lessons/DSC-0030-studio-scene-pack-runtime-binary-contract/LSN-0046-canonical-scene-owns-editorial-truth-while-pack-stays-request-driven.md","status":"done","created_at":"2026-05-01","updated_at":"2026-05-01"}]}
{"type":"discussion","id":"DSC-0029","status":"done","ticket":"studio-frame-composer-syscall-and-sprite-alignment","title":"Studio Alignment with Runtime FrameComposer Syscalls and Sprite Composition","created_at":"2026-04-18","updated_at":"2026-04-18","tags":["studio","compiler","pbs","stdlib","runtime-alignment","abi","syscall","frame-composer","sprites"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0041","file":"discussion/lessons/DSC-0029-studio-frame-composer-syscall-and-sprite-alignment/LSN-0041-composer-must-own-public-sprite-composition.md","status":"done","created_at":"2026-04-18","updated_at":"2026-04-18"}]}
{"type":"discussion","id":"DSC-0028","status":"done","ticket":"studio-tiled-parser-assets-scene-asset-type","title":"Tiled Parser and Scene Asset-Type Ownership in Assets Workspace","created_at":"2026-04-15","updated_at":"2026-04-23","tags":["studio","assets","scene","tiled","parser","asset-type"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0045","file":"discussion/lessons/DSC-0028-studio-tiled-parser-assets-scene-asset-type/LSN-0045-asset-owned-scene-bank-and-tiled-xml-workflow.md","status":"done","created_at":"2026-04-23","updated_at":"2026-04-23"}]}
{"type":"discussion","id":"DSC-0027","status":"abandoned","ticket":"studio-scene-workspace","title":"Scene Workspace for SCENE Authoring","created_at":"2026-04-14","updated_at":"2026-04-15","tags":["studio","workspace","scene","tilemap","asset","runtime-alignment"],"agendas":[{"id":"AGD-0029","file":"AGD-0029-studio-scene-workspace.md","status":"abandoned","created_at":"2026-04-14","updated_at":"2026-04-15","_override_reason":"Explicit user request on 2026-04-15 to abandon the accepted agenda and its downstream work."}],"decisions":[{"id":"DEC-0026","file":"DEC-0026-studio-scene-workspace.md","status":"abandoned","created_at":"2026-04-14","updated_at":"2026-04-15","ref_agenda":"AGD-0029","_override_reason":"Explicit user request on 2026-04-15 to abandon the accepted decision and stop using it as normative guidance."}],"plans":[{"id":"PLN-0049","file":"PLN-0049-scene-workspace-spec-and-boundary-propagation.md","status":"abandoned","created_at":"2026-04-14","updated_at":"2026-04-15","ref_decisions":["DEC-0026"],"_override_reason":"Explicit user request on 2026-04-15 to abandon all plans derived from DEC-0026."},{"id":"PLN-0050","file":"PLN-0050-scene-workspace-shell-and-project-state-foundations.md","status":"abandoned","created_at":"2026-04-14","updated_at":"2026-04-15","ref_decisions":["DEC-0026"],"_override_reason":"Explicit user request on 2026-04-15 to abandon all plans derived from DEC-0026."},{"id":"PLN-0051","file":"PLN-0051-scene-artifact-and-assets-handoff-contract.md","status":"abandoned","created_at":"2026-04-14","updated_at":"2026-04-15","ref_decisions":["DEC-0026"],"_override_reason":"Explicit user request on 2026-04-15 to abandon all plans derived from DEC-0026."},{"id":"PLN-0052","file":"PLN-0052-scene-workspace-wave-1-tilemap-editor.md","status":"abandoned","created_at":"2026-04-14","updated_at":"2026-04-15","ref_decisions":["DEC-0026"],"_override_reason":"Explicit user request on 2026-04-15 to abandon all plans derived from DEC-0026."}],"lessons":[]}
{"type":"discussion","id":"DSC-0026","status":"done","ticket":"glyph-bank-naming-alignment-with-runtime","title":"Glyph Bank Naming Alignment with Runtime DEC-0006","created_at":"2026-04-10","updated_at":"2026-04-10","tags":["packer","studio","naming","asset-contract","runtime-alignment","glyph-bank"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0040","file":"discussion/lessons/DSC-0026-glyph-bank-naming-alignment-with-runtime/LSN-0040-glyph-bank-artifact-naming-alignment.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]}
{"type":"discussion","id":"DSC-0025","status":"done","ticket":"packer-pipeline-metadata-ownership","title":"Pipeline Metadata Ownership and Runtime Contract","created_at":"2026-04-09","updated_at":"2026-04-10","tags":["packer","metadata","runtime-contract","tooling","studio"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0039","file":"discussion/lessons/DSC-0025-packer-pipeline-metadata-ownership/LSN-0039-runtime-header-boundary-and-tooling-owned-pipeline-metadata.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]}
{"type":"discussion","id":"DSC-0024","status":"done","ticket":"jacoco-reports-consolidation","title":"JaCoCo Reports Consolidation in Gradle","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["infra","gradle","jacoco","coverage","jenkins"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0038","file":"discussion/lessons/DSC-0024-jacoco-reports-consolidation/LSN-0038-jacoco-reports-consolidation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"},{"id":"LSN-0044","file":"discussion/lessons/DSC-0024-jacoco-reports-consolidation/LSN-0044-jenkins-jacoco-alignment.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]}
{"type":"discussion","id":"DSC-0001","status":"done","ticket":"studio-docs-import","title":"Import docs/studio into discussion-framework artifacts","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["studio","migration","discussion-framework","docs-import"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0001","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0001-assets-workspace-execution-wave-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0002","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0002-bank-composition-editor-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0003","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0003-mental-model-asset-mutations-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0004","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0004-mental-model-assets-workspace-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0005","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0005-mental-model-studio-events-and-components-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0006","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0006-mental-model-studio-shell-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0007","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0007-pack-wizard-shell-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0008","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0008-project-scoped-state-and-activity-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0016","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0016-studio-docs-import-pattern.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"}]}
{"type":"discussion","id":"DSC-0002","status":"done","ticket":"palette-management-in-studio","title":"Palette Management in Studio","created_at":"2026-03-26","updated_at":"2026-04-23","tags":["studio","legacy-import","palette-management","tile-bank","packer-boundary"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0042","file":"discussion/lessons/DSC-0002-palette-management-in-studio/LSN-0042-schema-driven-palette-authoring-and-local-metadata-events.md","status":"done","created_at":"2026-04-23","updated_at":"2026-04-23"}]}
{"type":"discussion","id":"DSC-0003","status":"done","ticket":"packer-docs-import","title":"Import docs/packer into discussion-framework artifacts","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["packer","migration","discussion-framework","docs-import"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0009","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0009-mental-model-packer-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0010","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0010-asset-identity-and-runtime-contract-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0011","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0011-foundations-workspace-runtime-and-build-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0012","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0012-runtime-ownership-and-studio-boundary-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0013","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0013-metadata-convergence-and-runtime-sink-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0014","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0014-pack-wizard-summary-validation-and-pack-execution-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0015","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0015-tile-bank-packing-contract-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0017","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0017-packer-docs-import-pattern.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"}]}
{"type":"discussion","id":"DSC-0004","status":"abandoned","ticket":"tilemap-and-metatile-runtime-binary-layout","title":"Tilemap and Metatile Runtime Binary Layout","created_at":"2026-03-26","updated_at":"2026-04-24","tags":["packer","legacy-import","tilemap","metatile","runtime-layout"],"agendas":[{"id":"AGD-0004","file":"AGD-0004-tilemap-and-metatile-runtime-binary-layout.md","status":"abandoned","created_at":"2026-03-26","updated_at":"2026-04-24","_override_reason":"Explicit user request on 2026-04-24 to abandon AGD-0004 because the agenda is no longer valid."}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0005","status":"open","ticket":"variable-tile-bank-palette-serialization","title":"Variable Tile Bank Palette Serialization","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["packer","legacy-import","tile-bank","palette-serialization","versioning"],"agendas":[{"id":"AGD-0005","file":"AGD-0005-variable-tile-bank-palette-serialization.md","status":"open","created_at":"2026-03-26","updated_at":"2026-03-26"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0006","status":"done","ticket":"pbs-game-facing-asset-refs-and-call-result-discard","title":"PBS Game-Facing Asset References and Ignored Call Result Lowering","created_at":"2026-03-27","updated_at":"2026-03-30","tags":["compiler","pbs","ergonomics","lowering","runtime","asset-identity","expression-statements"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0024","file":"discussion/lessons/DSC-0006-pbs-game-facing-asset-refs-and-call-result-discard/LSN-0024-addressable-surface-host-metadata-and-ignored-value-discipline.md","status":"done","created_at":"2026-03-30","updated_at":"2026-03-30"}]}
{"type":"discussion","id":"DSC-0007","status":"done","ticket":"pbs-learn-to-discussion-lessons-migration","title":"Migrate PBS Learn Documents into Discussion Lessons","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["compiler","pbs","migration","discussion-framework","lessons","learn-prune"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0018","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0018-pbs-ast-and-parser-contract-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0019","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0019-pbs-name-resolution-and-linking-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0020","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0020-pbs-runtime-values-identity-memory-boundaries-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0021","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0021-pbs-diagnostics-and-conformance-governance-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0022","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0022-pbs-globals-lifecycle-and-published-entrypoint-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]}
{"type":"discussion","id":"DSC-0008","status":"done","ticket":"pbs-low-level-asset-manager-surface","title":"PBS Low-Level Asset Manager Surface for Runtime AssetManager","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["compiler","pbs","runtime","asset-manager","host-abi","stdlib","asset"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0023","file":"discussion/lessons/DSC-0008-pbs-low-level-asset-manager-surface/LSN-0023-lowassets-runtime-aligned-sdk-surface.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]}
{"type":"discussion","id":"DSC-0009","status":"done","ticket":"studio-debugger-workspace-integration","title":"Integrate ../debugger into Studio as a dedicated workspace","created_at":"2026-03-30","updated_at":"2026-04-07","tags":["studio","debugger","workspace","integration","shell"],"agendas":[],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0010","status":"done","ticket":"studio-code-editor-workspace-foundations","title":"Establish Code Editor workspace foundations in Studio without LSP","created_at":"2026-03-30","updated_at":"2026-03-31","tags":["studio","editor","workspace","multi-frontend","lsp-deferred"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0026","file":"discussion/lessons/DSC-0010-studio-code-editor-workspace-foundations/LSN-0026-read-only-editor-foundations-and-semantic-deferral.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31"}]}
{"type":"discussion","id":"DSC-0011","status":"done","ticket":"compiler-analyze-compile-build-pipeline-split","title":"Split compiler pipeline into analyze, compile, and build entrypoints","created_at":"2026-03-30","updated_at":"2026-03-30","tags":["compiler","pipeline","artifacts","build","analysis"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0025","file":"discussion/lessons/DSC-0011-compiler-analyze-compile-build-pipeline-split/LSN-0025-compiler-pipeline-entrypoints-and-result-boundaries.md","status":"done","created_at":"2026-03-30","updated_at":"2026-03-30"}]}
{"type":"discussion","id":"DSC-0012","status":"done","ticket":"studio-editor-document-vfs-boundary","title":"Definir um boundary de VFS documental para tree/view/open files no Code Editor do Studio","created_at":"2026-03-31","updated_at":"2026-03-31","tags":["studio","editor","workspace","vfs","filesystem","boundary"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0027","file":"discussion/lessons/DSC-0012-studio-editor-document-vfs-boundary/LSN-0027-project-document-vfs-and-session-owned-editor-boundary.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31"}]}
{"type":"discussion","id":"DSC-0013","status":"done","ticket":"studio-editor-write-wave-supported-non-frontend-files","title":"Definir a wave inicial de edicao no Code Editor apenas para arquivos aceitos e nao relacionados ao FE","created_at":"2026-03-31","updated_at":"2026-04-02","tags":["studio","editor","workspace","write","read-only","vfs","frontend-boundary"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0028","file":"discussion/lessons/DSC-0013-studio-editor-write-wave-supported-non-frontend-files/LSN-0028-controlled-editor-write-wave-and-read-only-frontend-semantic-phase.md","status":"done","created_at":"2026-04-02","updated_at":"2026-04-02"}]}
{"type":"discussion","id":"DSC-0014","status":"done","ticket":"studio-frontend-owned-semantic-editor-presentation","title":"Definir ownership do schema visual semantico do editor por frontend","created_at":"2026-04-02","updated_at":"2026-04-02","tags":["studio","editor","frontend","presentation","semantic-highlighting","compiler","pbs"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0029","file":"discussion/lessons/DSC-0014-studio-frontend-owned-semantic-editor-presentation/LSN-0029-frontend-owned-semantic-presentation-descriptor-and-host-consumption.md","status":"done","created_at":"2026-04-02","updated_at":"2026-04-02"}]}
{"type":"discussion","id":"DSC-0015","status":"done","ticket":"pbs-service-facade-reserved-metadata","title":"SDK Service Bodies Calling Builtin/Intrinsic Proxies as Ordinary PBS Code","created_at":"2026-04-03","updated_at":"2026-04-03","tags":["compiler","pbs","sdk","stdlib","lowering","service","intrinsic","sdk-interface"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0030","file":"discussion/lessons/DSC-0015-pbs-service-facade-reserved-metadata/LSN-0030-sdk-service-bodies-over-private-reserved-proxies.md","status":"done","created_at":"2026-04-03","updated_at":"2026-04-03"}]}
{"type":"discussion","id":"DSC-0016","status":"done","ticket":"studio-editor-scope-guides-and-brace-anchoring","title":"Scope Guides do Code Editor com ancoragem exata em braces e destaque do escopo ativo","created_at":"2026-04-03","updated_at":"2026-04-06","tags":["studio","editor","scope-guides","braces","semantic-read","frontend-contract"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0034","file":"discussion/lessons/DSC-0016-studio-editor-scope-guides-and-brace-anchoring/LSN-0034-active-scope-gutter-and-frontend-owned-structural-anchors.md","status":"done","created_at":"2026-04-06","updated_at":"2026-04-06"}]}
{"type":"discussion","id":"DSC-0017","status":"done","ticket":"studio-editor-inline-type-hints-for-let-bindings","title":"Inline Type Hints for Let Bindings in the Studio Editor","created_at":"2026-04-03","updated_at":"2026-04-06","tags":["studio","editor","inline-hints","inlay-hints","lsp","pbs","type-inference"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0035","file":"discussion/lessons/DSC-0017-studio-editor-inline-type-hints-for-let-bindings/LSN-0035-frontend-owned-inline-hints-with-decorative-inline-rendering.md","status":"done","created_at":"2026-04-06","updated_at":"2026-04-06"}]}
{"type":"discussion","id":"DSC-0018","status":"done","ticket":"studio-project-local-studio-state-under-dot-studio","title":"Persist project-local Studio state under .studio","created_at":"2026-04-04","updated_at":"2026-04-04","tags":["studio","project-session","project-state","persistence","dot-studio","shell","layout","setup"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0031","file":"discussion/lessons/DSC-0018-studio-project-local-studio-state-under-dot-studio/LSN-0031-project-local-studio-state-and-lifecycle-safe-layout-restoration.md","status":"done","created_at":"2026-04-04","updated_at":"2026-04-04"}]}
{"type":"discussion","id":"DSC-0019","status":"done","ticket":"studio-project-local-setup-separate-from-main-studio-state","title":"Separate project-local setup from the main Studio state under .studio","created_at":"2026-04-04","updated_at":"2026-04-04","tags":["studio","project-session","project-state","persistence","dot-studio","setup","boundary"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0032","file":"discussion/lessons/DSC-0019-studio-project-local-setup-separate-from-main-studio-state/LSN-0032-separate-project-config-from-session-restoration-state.md","status":"done","created_at":"2026-04-04","updated_at":"2026-04-04"}]}
{"type":"discussion","id":"DSC-0020","status":"done","ticket":"studio-editor-indentation-policy-and-project-setup","title":"Indentation Policy, Status-Bar Semantics, and Project-Local Editor Setup","created_at":"2026-04-04","updated_at":"2026-04-04","tags":["studio","editor","indentation","tabs","setup","dot-studio","configuration","ux"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0033","file":"discussion/lessons/DSC-0020-studio-editor-indentation-policy-and-project-setup/LSN-0033-setup-owned-indentation-policy-and-project-bootstrap-defaults.md","status":"done","created_at":"2026-04-04","updated_at":"2026-04-04"}]}
{"type":"discussion","id":"DSC-0021","status":"done","ticket":"studio-frontend-editor-write-and-save-wave","title":"Enable Frontend Editing and Save in the Studio Code Editor","created_at":"2026-04-04","updated_at":"2026-04-06","tags":["studio","editor","frontend","write","save","vfs","lsp","pbs","access-policy"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0036","file":"discussion/lessons/DSC-0021-studio-frontend-editor-write-and-save-wave/LSN-0036-vfs-owned-frontend-edit-and-save-wave.md","status":"done","created_at":"2026-04-06","updated_at":"2026-04-06"}]}
{"type":"discussion","id":"DSC-0022","status":"done","ticket":"studio-play-stop-cartridge-flow","title":"Wire the Studio Play/Stop button to shipper phase 1 cartridge preparation and runtime execution","created_at":"2026-04-06","updated_at":"2026-04-07","tags":["studio","play-stop","shipper","runtime","cartridge","shell"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0043","file":"discussion/lessons/DSC-0022-studio-play-stop-and-debugger-integration/LSN-0043-studio-play-stop-and-debugger-integration.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]}
{"type":"discussion","id":"DSC-0023","status":"done","ticket":"gradle-version-catalog-migration","title":"Move dependency version management to Version Catalog (TOML)","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["gradle","buildSrc","dependency-management","version-catalog"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0037","file":"discussion/lessons/DSC-0023-gradle-version-catalog-migration/LSN-0037-gradle-version-catalog-adoption.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]}

View File

@ -0,0 +1,35 @@
{"type":"meta","next_id":{"DSC":35,"AGD":38,"DEC":35,"PLN":75,"LSN":49,"CLSN":1}}
{"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":"in_progress","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-06","tags":["compiler","compiler-general","frontend","presentation","theming","studio","vscode","lsp","pbs"],"agendas":[{"id":"AGD-0036","file":"AGD-0036-frontend-visual-theme-spec-and-css-retirement.md","status":"accepted","created_at":"2026-05-06","updated_at":"2026-05-06"}],"decisions":[{"id":"DEC-0033","file":"DEC-0033-frontend-visual-theme-spec-and-css-retirement.md","status":"accepted","created_at":"2026-05-06","updated_at":"2026-05-06","ref_agenda":"AGD-0036"}],"plans":[{"id":"PLN-0068","file":"PLN-0068-frontend-visual-contract-and-pbs-migration.md","status":"open","created_at":"2026-05-06","updated_at":"2026-05-06","ref_decisions":["DEC-0033"]},{"id":"PLN-0069","file":"PLN-0069-lsp-visual-theme-transport-and-selection.md","status":"open","created_at":"2026-05-06","updated_at":"2026-05-06","ref_decisions":["DEC-0033"]},{"id":"PLN-0070","file":"PLN-0070-studio-visual-theme-adapter-and-css-retirement.md","status":"open","created_at":"2026-05-06","updated_at":"2026-05-06","ref_decisions":["DEC-0033"]},{"id":"PLN-0071","file":"PLN-0071-vscode-dynamic-theme-translation-adapter.md","status":"open","created_at":"2026-05-06","updated_at":"2026-05-06","ref_decisions":["DEC-0033"]}],"lessons":[]}
{"type":"discussion","id":"DSC-0032","status":"done","ticket":"studio-new-lsp-api-and-v1-boundary","title":"Novo boundary entre lsp-api, lsp-v1 e a extensao VS Code","created_at":"2026-05-05","updated_at":"2026-05-07","tags":["studio","lsp","vscode","protocol","api","boundary"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0047","file":"discussion/lessons/DSC-0032-studio-new-lsp-api-and-v1-boundary/LSN-0047-project-scoped-lsp-boundary-and-protocol-containment.md","status":"done","created_at":"2026-05-07","updated_at":"2026-05-07"}]}
{"type":"discussion","id":"DSC-0031","status":"in_progress","ticket":"studio-editor-workspace-cleanup","title":"Limpeza completa do Workspace Editor do Studio e remoção dos acoplamentos legados","created_at":"2026-05-05","updated_at":"2026-05-05","tags":["studio","editor","cleanup","vfs","lsp","migration"],"agendas":[{"id":"AGD-0034","file":"AGD-0034-vscode-editor-migration-feasibility.md","status":"accepted","created_at":"2026-05-05","updated_at":"2026-05-05"}],"decisions":[{"id":"DEC-0031","file":"DEC-0031-studio-editor-workspace-cleanup.md","status":"in_progress","created_at":"2026-05-05","updated_at":"2026-05-05","ref_agenda":"AGD-0034"}],"plans":[{"id":"PLN-0062","file":"PLN-0062-shell-session-and-state-cleanup-after-editor-removal.md","status":"done","created_at":"2026-05-05","updated_at":"2026-05-05","ref_decisions":["DEC-0031"]},{"id":"PLN-0063","file":"PLN-0063-legacy-lsp-and-vfs-module-removal-with-runtime-preservation.md","status":"done","created_at":"2026-05-05","updated_at":"2026-05-05","ref_decisions":["DEC-0031"]},{"id":"PLN-0064","file":"PLN-0064-normative-test-and-lesson-cleanup-for-editor-stack-removal.md","status":"done","created_at":"2026-05-05","updated_at":"2026-05-05","ref_decisions":["DEC-0031"]}],"lessons":[]}
{"type":"discussion","id":"DSC-0030","status":"done","ticket":"studio-scene-pack-runtime-binary-contract","title":"Studio scene pack contract for runtime SCENE binary payload","created_at":"2026-04-24","updated_at":"2026-05-01","tags":["studio","packer","runtime","scene","asset-pack","binary-format","tiled"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0046","file":"discussion/lessons/DSC-0030-studio-scene-pack-runtime-binary-contract/LSN-0046-canonical-scene-owns-editorial-truth-while-pack-stays-request-driven.md","status":"done","created_at":"2026-05-01","updated_at":"2026-05-01"}]}
{"type":"discussion","id":"DSC-0029","status":"done","ticket":"studio-frame-composer-syscall-and-sprite-alignment","title":"Studio Alignment with Runtime FrameComposer Syscalls and Sprite Composition","created_at":"2026-04-18","updated_at":"2026-04-18","tags":["studio","compiler","pbs","stdlib","runtime-alignment","abi","syscall","frame-composer","sprites"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0041","file":"discussion/lessons/DSC-0029-studio-frame-composer-syscall-and-sprite-alignment/LSN-0041-composer-must-own-public-sprite-composition.md","status":"done","created_at":"2026-04-18","updated_at":"2026-04-18"}]}
{"type":"discussion","id":"DSC-0028","status":"done","ticket":"studio-tiled-parser-assets-scene-asset-type","title":"Tiled Parser and Scene Asset-Type Ownership in Assets Workspace","created_at":"2026-04-15","updated_at":"2026-04-23","tags":["studio","assets","scene","tiled","parser","asset-type"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0045","file":"discussion/lessons/DSC-0028-studio-tiled-parser-assets-scene-asset-type/LSN-0045-asset-owned-scene-bank-and-tiled-xml-workflow.md","status":"done","created_at":"2026-04-23","updated_at":"2026-04-23"}]}
{"type":"discussion","id":"DSC-0027","status":"abandoned","ticket":"studio-scene-workspace","title":"Scene Workspace for SCENE Authoring","created_at":"2026-04-14","updated_at":"2026-04-15","tags":["studio","workspace","scene","tilemap","asset","runtime-alignment"],"agendas":[{"id":"AGD-0029","file":"AGD-0029-studio-scene-workspace.md","status":"abandoned","created_at":"2026-04-14","updated_at":"2026-04-15","_override_reason":"Explicit user request on 2026-04-15 to abandon the accepted agenda and its downstream work."}],"decisions":[{"id":"DEC-0026","file":"DEC-0026-studio-scene-workspace.md","status":"abandoned","created_at":"2026-04-14","updated_at":"2026-04-15","ref_agenda":"AGD-0029","_override_reason":"Explicit user request on 2026-04-15 to abandon the accepted decision and stop using it as normative guidance."}],"plans":[{"id":"PLN-0049","file":"PLN-0049-scene-workspace-spec-and-boundary-propagation.md","status":"abandoned","created_at":"2026-04-14","updated_at":"2026-04-15","ref_decisions":["DEC-0026"],"_override_reason":"Explicit user request on 2026-04-15 to abandon all plans derived from DEC-0026."},{"id":"PLN-0050","file":"PLN-0050-scene-workspace-shell-and-project-state-foundations.md","status":"abandoned","created_at":"2026-04-14","updated_at":"2026-04-15","ref_decisions":["DEC-0026"],"_override_reason":"Explicit user request on 2026-04-15 to abandon all plans derived from DEC-0026."},{"id":"PLN-0051","file":"PLN-0051-scene-artifact-and-assets-handoff-contract.md","status":"abandoned","created_at":"2026-04-14","updated_at":"2026-04-15","ref_decisions":["DEC-0026"],"_override_reason":"Explicit user request on 2026-04-15 to abandon all plans derived from DEC-0026."},{"id":"PLN-0052","file":"PLN-0052-scene-workspace-wave-1-tilemap-editor.md","status":"abandoned","created_at":"2026-04-14","updated_at":"2026-04-15","ref_decisions":["DEC-0026"],"_override_reason":"Explicit user request on 2026-04-15 to abandon all plans derived from DEC-0026."}],"lessons":[]}
{"type":"discussion","id":"DSC-0026","status":"done","ticket":"glyph-bank-naming-alignment-with-runtime","title":"Glyph Bank Naming Alignment with Runtime DEC-0006","created_at":"2026-04-10","updated_at":"2026-04-10","tags":["packer","studio","naming","asset-contract","runtime-alignment","glyph-bank"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0040","file":"discussion/lessons/DSC-0026-glyph-bank-naming-alignment-with-runtime/LSN-0040-glyph-bank-artifact-naming-alignment.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]}
{"type":"discussion","id":"DSC-0025","status":"done","ticket":"packer-pipeline-metadata-ownership","title":"Pipeline Metadata Ownership and Runtime Contract","created_at":"2026-04-09","updated_at":"2026-04-10","tags":["packer","metadata","runtime-contract","tooling","studio"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0039","file":"discussion/lessons/DSC-0025-packer-pipeline-metadata-ownership/LSN-0039-runtime-header-boundary-and-tooling-owned-pipeline-metadata.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]}
{"type":"discussion","id":"DSC-0024","status":"done","ticket":"jacoco-reports-consolidation","title":"JaCoCo Reports Consolidation in Gradle","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["infra","gradle","jacoco","coverage","jenkins"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0038","file":"discussion/lessons/DSC-0024-jacoco-reports-consolidation/LSN-0038-jacoco-reports-consolidation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"},{"id":"LSN-0044","file":"discussion/lessons/DSC-0024-jacoco-reports-consolidation/LSN-0044-jenkins-jacoco-alignment.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]}
{"type":"discussion","id":"DSC-0001","status":"done","ticket":"studio-docs-import","title":"Import docs/studio into discussion-framework artifacts","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["studio","migration","discussion-framework","docs-import"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0001","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0001-assets-workspace-execution-wave-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0002","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0002-bank-composition-editor-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0003","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0003-mental-model-asset-mutations-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0004","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0004-mental-model-assets-workspace-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0005","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0005-mental-model-studio-events-and-components-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0006","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0006-mental-model-studio-shell-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0007","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0007-pack-wizard-shell-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0008","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0008-project-scoped-state-and-activity-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0016","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0016-studio-docs-import-pattern.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"}]}
{"type":"discussion","id":"DSC-0002","status":"done","ticket":"palette-management-in-studio","title":"Palette Management in Studio","created_at":"2026-03-26","updated_at":"2026-04-23","tags":["studio","legacy-import","palette-management","tile-bank","packer-boundary"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0042","file":"discussion/lessons/DSC-0002-palette-management-in-studio/LSN-0042-schema-driven-palette-authoring-and-local-metadata-events.md","status":"done","created_at":"2026-04-23","updated_at":"2026-04-23"}]}
{"type":"discussion","id":"DSC-0003","status":"done","ticket":"packer-docs-import","title":"Import docs/packer into discussion-framework artifacts","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["packer","migration","discussion-framework","docs-import"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0009","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0009-mental-model-packer-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0010","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0010-asset-identity-and-runtime-contract-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0011","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0011-foundations-workspace-runtime-and-build-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0012","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0012-runtime-ownership-and-studio-boundary-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0013","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0013-metadata-convergence-and-runtime-sink-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0014","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0014-pack-wizard-summary-validation-and-pack-execution-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0015","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0015-tile-bank-packing-contract-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0017","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0017-packer-docs-import-pattern.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"}]}
{"type":"discussion","id":"DSC-0004","status":"abandoned","ticket":"tilemap-and-metatile-runtime-binary-layout","title":"Tilemap and Metatile Runtime Binary Layout","created_at":"2026-03-26","updated_at":"2026-04-24","tags":["packer","legacy-import","tilemap","metatile","runtime-layout"],"agendas":[{"id":"AGD-0004","file":"AGD-0004-tilemap-and-metatile-runtime-binary-layout.md","status":"abandoned","created_at":"2026-03-26","updated_at":"2026-04-24","_override_reason":"Explicit user request on 2026-04-24 to abandon AGD-0004 because the agenda is no longer valid."}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0005","status":"open","ticket":"variable-tile-bank-palette-serialization","title":"Variable Tile Bank Palette Serialization","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["packer","legacy-import","tile-bank","palette-serialization","versioning"],"agendas":[{"id":"AGD-0005","file":"AGD-0005-variable-tile-bank-palette-serialization.md","status":"open","created_at":"2026-03-26","updated_at":"2026-03-26"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0006","status":"done","ticket":"pbs-game-facing-asset-refs-and-call-result-discard","title":"PBS Game-Facing Asset References and Ignored Call Result Lowering","created_at":"2026-03-27","updated_at":"2026-03-30","tags":["compiler","pbs","ergonomics","lowering","runtime","asset-identity","expression-statements"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0024","file":"discussion/lessons/DSC-0006-pbs-game-facing-asset-refs-and-call-result-discard/LSN-0024-addressable-surface-host-metadata-and-ignored-value-discipline.md","status":"done","created_at":"2026-03-30","updated_at":"2026-03-30"}]}
{"type":"discussion","id":"DSC-0007","status":"done","ticket":"pbs-learn-to-discussion-lessons-migration","title":"Migrate PBS Learn Documents into Discussion Lessons","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["compiler","pbs","migration","discussion-framework","lessons","learn-prune"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0018","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0018-pbs-ast-and-parser-contract-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0019","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0019-pbs-name-resolution-and-linking-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0020","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0020-pbs-runtime-values-identity-memory-boundaries-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0021","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0021-pbs-diagnostics-and-conformance-governance-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0022","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0022-pbs-globals-lifecycle-and-published-entrypoint-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]}
{"type":"discussion","id":"DSC-0008","status":"done","ticket":"pbs-low-level-asset-manager-surface","title":"PBS Low-Level Asset Manager Surface for Runtime AssetManager","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["compiler","pbs","runtime","asset-manager","host-abi","stdlib","asset"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0023","file":"discussion/lessons/DSC-0008-pbs-low-level-asset-manager-surface/LSN-0023-lowassets-runtime-aligned-sdk-surface.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]}
{"type":"discussion","id":"DSC-0009","status":"done","ticket":"studio-debugger-workspace-integration","title":"Integrate ../debugger into Studio as a dedicated workspace","created_at":"2026-03-30","updated_at":"2026-04-07","tags":["studio","debugger","workspace","integration","shell"],"agendas":[],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0010","status":"done","ticket":"studio-code-editor-workspace-foundations","title":"Establish Code Editor workspace foundations in Studio without LSP","created_at":"2026-03-30","updated_at":"2026-03-31","tags":["studio","editor","workspace","multi-frontend","lsp-deferred"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0026","file":"discussion/lessons/DSC-0010-studio-code-editor-workspace-foundations/LSN-0026-read-only-editor-foundations-and-semantic-deferral.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31"}]}
{"type":"discussion","id":"DSC-0011","status":"done","ticket":"compiler-analyze-compile-build-pipeline-split","title":"Split compiler pipeline into analyze, compile, and build entrypoints","created_at":"2026-03-30","updated_at":"2026-03-30","tags":["compiler","pipeline","artifacts","build","analysis"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0025","file":"discussion/lessons/DSC-0011-compiler-analyze-compile-build-pipeline-split/LSN-0025-compiler-pipeline-entrypoints-and-result-boundaries.md","status":"done","created_at":"2026-03-30","updated_at":"2026-03-30"}]}
{"type":"discussion","id":"DSC-0012","status":"done","ticket":"studio-editor-document-vfs-boundary","title":"Definir um boundary de VFS documental para tree/view/open files no Code Editor do Studio","created_at":"2026-03-31","updated_at":"2026-03-31","tags":["studio","editor","workspace","vfs","filesystem","boundary"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0027","file":"discussion/lessons/DSC-0012-studio-editor-document-vfs-boundary/LSN-0027-project-document-vfs-and-session-owned-editor-boundary.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31"}]}
{"type":"discussion","id":"DSC-0013","status":"done","ticket":"studio-editor-write-wave-supported-non-frontend-files","title":"Definir a wave inicial de edicao no Code Editor apenas para arquivos aceitos e nao relacionados ao FE","created_at":"2026-03-31","updated_at":"2026-04-02","tags":["studio","editor","workspace","write","read-only","vfs","frontend-boundary"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0028","file":"discussion/lessons/DSC-0013-studio-editor-write-wave-supported-non-frontend-files/LSN-0028-controlled-editor-write-wave-and-read-only-frontend-semantic-phase.md","status":"done","created_at":"2026-04-02","updated_at":"2026-04-02"}]}
{"type":"discussion","id":"DSC-0014","status":"done","ticket":"studio-frontend-owned-semantic-editor-presentation","title":"Definir ownership do schema visual semantico do editor por frontend","created_at":"2026-04-02","updated_at":"2026-04-02","tags":["studio","editor","frontend","presentation","semantic-highlighting","compiler","pbs"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0029","file":"discussion/lessons/DSC-0014-studio-frontend-owned-semantic-editor-presentation/LSN-0029-frontend-owned-semantic-presentation-descriptor-and-host-consumption.md","status":"done","created_at":"2026-04-02","updated_at":"2026-04-02"}]}
{"type":"discussion","id":"DSC-0015","status":"done","ticket":"pbs-service-facade-reserved-metadata","title":"SDK Service Bodies Calling Builtin/Intrinsic Proxies as Ordinary PBS Code","created_at":"2026-04-03","updated_at":"2026-04-03","tags":["compiler","pbs","sdk","stdlib","lowering","service","intrinsic","sdk-interface"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0030","file":"discussion/lessons/DSC-0015-pbs-service-facade-reserved-metadata/LSN-0030-sdk-service-bodies-over-private-reserved-proxies.md","status":"done","created_at":"2026-04-03","updated_at":"2026-04-03"}]}
{"type":"discussion","id":"DSC-0016","status":"done","ticket":"studio-editor-scope-guides-and-brace-anchoring","title":"Scope Guides do Code Editor com ancoragem exata em braces e destaque do escopo ativo","created_at":"2026-04-03","updated_at":"2026-04-06","tags":["studio","editor","scope-guides","braces","semantic-read","frontend-contract"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0034","file":"discussion/lessons/DSC-0016-studio-editor-scope-guides-and-brace-anchoring/LSN-0034-active-scope-gutter-and-frontend-owned-structural-anchors.md","status":"done","created_at":"2026-04-06","updated_at":"2026-04-06"}]}
{"type":"discussion","id":"DSC-0017","status":"done","ticket":"studio-editor-inline-type-hints-for-let-bindings","title":"Inline Type Hints for Let Bindings in the Studio Editor","created_at":"2026-04-03","updated_at":"2026-04-06","tags":["studio","editor","inline-hints","inlay-hints","lsp","pbs","type-inference"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0035","file":"discussion/lessons/DSC-0017-studio-editor-inline-type-hints-for-let-bindings/LSN-0035-frontend-owned-inline-hints-with-decorative-inline-rendering.md","status":"done","created_at":"2026-04-06","updated_at":"2026-04-06"}]}
{"type":"discussion","id":"DSC-0018","status":"done","ticket":"studio-project-local-studio-state-under-dot-studio","title":"Persist project-local Studio state under .studio","created_at":"2026-04-04","updated_at":"2026-04-04","tags":["studio","project-session","project-state","persistence","dot-studio","shell","layout","setup"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0031","file":"discussion/lessons/DSC-0018-studio-project-local-studio-state-under-dot-studio/LSN-0031-project-local-studio-state-and-lifecycle-safe-layout-restoration.md","status":"done","created_at":"2026-04-04","updated_at":"2026-04-04"}]}
{"type":"discussion","id":"DSC-0019","status":"done","ticket":"studio-project-local-setup-separate-from-main-studio-state","title":"Separate project-local setup from the main Studio state under .studio","created_at":"2026-04-04","updated_at":"2026-04-04","tags":["studio","project-session","project-state","persistence","dot-studio","setup","boundary"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0032","file":"discussion/lessons/DSC-0019-studio-project-local-setup-separate-from-main-studio-state/LSN-0032-separate-project-config-from-session-restoration-state.md","status":"done","created_at":"2026-04-04","updated_at":"2026-04-04"}]}
{"type":"discussion","id":"DSC-0020","status":"done","ticket":"studio-editor-indentation-policy-and-project-setup","title":"Indentation Policy, Status-Bar Semantics, and Project-Local Editor Setup","created_at":"2026-04-04","updated_at":"2026-04-04","tags":["studio","editor","indentation","tabs","setup","dot-studio","configuration","ux"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0033","file":"discussion/lessons/DSC-0020-studio-editor-indentation-policy-and-project-setup/LSN-0033-setup-owned-indentation-policy-and-project-bootstrap-defaults.md","status":"done","created_at":"2026-04-04","updated_at":"2026-04-04"}]}
{"type":"discussion","id":"DSC-0021","status":"done","ticket":"studio-frontend-editor-write-and-save-wave","title":"Enable Frontend Editing and Save in the Studio Code Editor","created_at":"2026-04-04","updated_at":"2026-04-06","tags":["studio","editor","frontend","write","save","vfs","lsp","pbs","access-policy"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0036","file":"discussion/lessons/DSC-0021-studio-frontend-editor-write-and-save-wave/LSN-0036-vfs-owned-frontend-edit-and-save-wave.md","status":"done","created_at":"2026-04-06","updated_at":"2026-04-06"}]}
{"type":"discussion","id":"DSC-0022","status":"done","ticket":"studio-play-stop-cartridge-flow","title":"Wire the Studio Play/Stop button to shipper phase 1 cartridge preparation and runtime execution","created_at":"2026-04-06","updated_at":"2026-04-07","tags":["studio","play-stop","shipper","runtime","cartridge","shell"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0043","file":"discussion/lessons/DSC-0022-studio-play-stop-and-debugger-integration/LSN-0043-studio-play-stop-and-debugger-integration.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]}
{"type":"discussion","id":"DSC-0023","status":"done","ticket":"gradle-version-catalog-migration","title":"Move dependency version management to Version Catalog (TOML)","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["gradle","buildSrc","dependency-management","version-catalog"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0037","file":"discussion/lessons/DSC-0023-gradle-version-catalog-migration/LSN-0037-gradle-version-catalog-adoption.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]}

View File

@ -1,4 +1,10 @@
{"type":"meta","next_id":{"DSC":31,"AGD":34,"DEC":31,"PLN":62,"LSN":47,"CLSN":1}}
{"type":"meta","next_id":{"DSC":37,"AGD":40,"DEC":36,"PLN":78,"LSN":51,"CLSN":1}}
{"type":"discussion","id":"DSC-0036","status":"open","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-05-08","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":"open","created_at":"2026-05-08","updated_at":"2026-05-08"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0035","status":"in_progress","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":[{"id":"AGD-0038","file":"AGD-0038-pbs-lsp-editor-assistance-wave-1.md","status":"accepted","created_at":"2026-05-08","updated_at":"2026-05-08"}],"decisions":[{"id":"DEC-0035","file":"DEC-0035-pbs-lsp-editor-assistance-wave-1.md","status":"accepted","created_at":"2026-05-08","updated_at":"2026-05-08","ref_agenda":"AGD-0038"}],"plans":[{"id":"PLN-0075","file":"PLN-0075-pbs-editorial-resolution-surface-for-symbols-members-and-signatures.md","status":"open","created_at":"2026-05-08","updated_at":"2026-05-08","ref_decisions":["DEC-0035"]},{"id":"PLN-0076","file":"PLN-0076-lsp-completion-hover-and-signature-help-over-editorial-resolution.md","status":"open","created_at":"2026-05-08","updated_at":"2026-05-08","ref_decisions":["DEC-0035"]},{"id":"PLN-0077","file":"PLN-0077-vscode-consumption-and-wave-1-editor-assistance-validation.md","status":"open","created_at":"2026-05-08","updated_at":"2026-05-08","ref_decisions":["DEC-0035"]}],"lessons":[]}
{"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"}]}
{"type":"discussion","id":"DSC-0032","status":"done","ticket":"studio-new-lsp-api-and-v1-boundary","title":"Novo boundary entre lsp-api, lsp-v1 e a extensao VS Code","created_at":"2026-05-05","updated_at":"2026-05-07","tags":["studio","lsp","vscode","protocol","api","boundary"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0047","file":"discussion/lessons/DSC-0032-studio-new-lsp-api-and-v1-boundary/LSN-0047-project-scoped-lsp-boundary-and-protocol-containment.md","status":"done","created_at":"2026-05-07","updated_at":"2026-05-07"}]}
{"type":"discussion","id":"DSC-0031","status":"done","ticket":"studio-editor-workspace-cleanup","title":"Limpeza completa do Workspace Editor do Studio e remoção dos acoplamentos legados","created_at":"2026-05-05","updated_at":"2026-05-08","tags":["studio","editor","cleanup","vfs","lsp","migration"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0049","file":"discussion/lessons/DSC-0031-studio-editor-workspace-cleanup/LSN-0049-remove-the-embedded-editor-stack-before-rebuilding-code-workflows.md","status":"done","created_at":"2026-05-08","updated_at":"2026-05-08"}]}
{"type":"discussion","id":"DSC-0030","status":"done","ticket":"studio-scene-pack-runtime-binary-contract","title":"Studio scene pack contract for runtime SCENE binary payload","created_at":"2026-04-24","updated_at":"2026-05-01","tags":["studio","packer","runtime","scene","asset-pack","binary-format","tiled"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0046","file":"discussion/lessons/DSC-0030-studio-scene-pack-runtime-binary-contract/LSN-0046-canonical-scene-owns-editorial-truth-while-pack-stays-request-driven.md","status":"done","created_at":"2026-05-01","updated_at":"2026-05-01"}]}
{"type":"discussion","id":"DSC-0029","status":"done","ticket":"studio-frame-composer-syscall-and-sprite-alignment","title":"Studio Alignment with Runtime FrameComposer Syscalls and Sprite Composition","created_at":"2026-04-18","updated_at":"2026-04-18","tags":["studio","compiler","pbs","stdlib","runtime-alignment","abi","syscall","frame-composer","sprites"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0041","file":"discussion/lessons/DSC-0029-studio-frame-composer-syscall-and-sprite-alignment/LSN-0041-composer-must-own-public-sprite-composition.md","status":"done","created_at":"2026-04-18","updated_at":"2026-04-18"}]}
{"type":"discussion","id":"DSC-0028","status":"done","ticket":"studio-tiled-parser-assets-scene-asset-type","title":"Tiled Parser and Scene Asset-Type Ownership in Assets Workspace","created_at":"2026-04-15","updated_at":"2026-04-23","tags":["studio","assets","scene","tiled","parser","asset-type"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0045","file":"discussion/lessons/DSC-0028-studio-tiled-parser-assets-scene-asset-type/LSN-0045-asset-owned-scene-bank-and-tiled-xml-workflow.md","status":"done","created_at":"2026-04-23","updated_at":"2026-04-23"}]}

View File

@ -6,6 +6,10 @@ created: 2026-03-31
tags: [studio, editor, workspace, read-only, navigator, tabs, lsp-deferred, ui-foundations]
---
## Historical Status
This lesson documents the removed embedded Studio code editor. Keep it as historical context only; do not treat it as current product guidance after `DEC-0031`.
## Context
Studio needed a real `Code Editor` workspace, but the team explicitly did not want the first wave to collapse UI structure, file management, write behavior, and language semantics into one premature implementation.

View File

@ -6,6 +6,10 @@ created: 2026-03-31
tags: [studio, editor, vfs, project-session, filesystem, boundary, architecture]
---
## Historical Status
This lesson documents the removed embedded Studio editor/VFS split. Keep it as historical context only; do not treat it as current product guidance after `DEC-0031`.
## Context
The first Studio `Code Editor` wave had already established a read-only editorial shell, but the concrete implementation still let the editor package own direct filesystem concerns for:

View File

@ -6,6 +6,10 @@ created: 2026-04-02
tags: [studio, editor, vfs, lsp, access-policy, save, read-only, frontend-boundary]
---
## Historical Status
This lesson documents the removed embedded Studio editor, VFS, and legacy LSP flow. Keep it as historical context only; do not treat it as current product guidance after `DEC-0031`.
## Context
The first Studio `Code Editor` implementation had already established a session-owned document boundary through `prometeu-vfs`, but it was still missing the next operational split:

View File

@ -6,6 +6,10 @@ created: 2026-04-02
tags: [studio, editor, frontend, semantic-highlighting, lsp, compiler, pbs, presentation]
---
## Historical Status
This lesson documents the removed embedded Studio semantic editor presentation path. Keep it as historical context only; do not treat it as current product guidance after `DEC-0031`.
## Context
The first semantic highlight wave for frontend documents had already proved that Studio could consume semantic spans through the integrated LSP path.

View File

@ -6,6 +6,10 @@ created: 2026-04-06
tags: [studio, editor, scope-guides, structural-anchors, semantic-read, frontend-contract]
---
## Historical Status
This lesson documents the removed embedded Studio editor gutter and structural-anchor flow. Keep it as historical context only; do not treat it as current product guidance after `DEC-0031`.
## Context
The Studio editor already exposed structural guides in the gutter, but the old behavior mixed two different concerns:

View File

@ -6,6 +6,10 @@ created: 2026-04-06
tags: [studio, editor, inline-hints, lsp, compiler, pbs, type-inference, rendering]
---
## Historical Status
This lesson documents the removed embedded Studio inline-hint path. Keep it as historical context only; do not treat it as current product guidance after `DEC-0031`.
## Context
The repository needed inline type hints for inferred PBS `let` bindings, but the real architectural issue was broader than one PBS feature.

View File

@ -6,6 +6,10 @@ created: 2026-04-04
tags: [studio, editor, indentation, tabs, setup, dot-studio, project-creation, gitignore]
---
## Historical Status
This lesson documents indentation policy choices made for the removed embedded Studio editor. Keep it as historical context only; do not treat it as current product guidance after `DEC-0031`.
## Context
The Studio editor exposed an indentation chip in the status bar, but that chip was driven by file-content heuristics rather than by a stable editor policy.

View File

@ -6,6 +6,10 @@ created: 2026-04-06
tags: [studio, editor, frontend, write, save, vfs, lsp, access-policy, persistence]
---
## Historical Status
This lesson documents the removed embedded Studio frontend edit/save wave. Keep it as historical context only; do not treat it as current product guidance after `DEC-0031`.
## Context
The first editor write wave had deliberately kept frontend files hard `read-only` while semantic-read support shipped through `prometeu-lsp`.

View File

@ -0,0 +1,84 @@
---
id: LSN-0049
discussion: DSC-0031
title: Remove the embedded editor stack before rebuilding code workflows
status: done
created: 2026-05-08
updated: 2026-05-08
tags: [studio, editor, cleanup, lsp, vfs, shell, migration]
---
## Original Problem
Studio still carried an embedded `Code Editor` workspace together with a `prometeu-vfs` boundary and a legacy `prometeu-lsp` path that existed only to support that editor.
That stack had become architectural drag:
- the JavaFX editor was no longer a product direction;
- the legacy `VFS` and `LSP` contracts were shaped around that editor model;
- shell state and project session still persisted editor-specific assumptions;
- and keeping those surfaces alive made any future code-backend redesign harder.
## Consolidated Decision
`DEC-0031` locked a full cleanup of the embedded editor stack.
The repository removed:
- the `Code Editor` workspace as an active Studio surface;
- the legacy editor-owned `VFS` boundary;
- the legacy editor-era `LSP` assumptions;
- shell/session/state dependencies that expected the editor to exist;
- and normative references that treated that stack as current architecture.
The decision also locked an equally important limit:
- this cleanup was not allowed to define the future code backend;
- a replacement LSP or automation boundary had to come later, in separate discussion.
## Final Result
The Studio shell now opens with surviving workspaces only: `Assets`, `Debug`, and `Shipper`.
Project-local state no longer persists editor restoration or editor layout fields, while still loading old state safely and falling back to surviving shell behavior.
The cleanup also succeeded without freezing the repository in a "no LSP ever again" state.
Later work in `DSC-0032` introduced a new project-scoped LSP boundary as separate architecture, which is exactly what `DEC-0031` required: remove the old stack first, then rebuild cleanly under a new decision.
## Implementation Notes
The cleanup landed in three coordinated waves:
1. shell/session/state cleanup removed `WorkspaceId.EDITOR`, editor restoration state, and editor-owned session composition;
2. the old editor-era `VFS` and `LSP` stack stopped being the active Studio foundation;
3. specs, tests, and historical references were revised so the removed editor stack no longer appears as current product guidance.
This produced an important boundary reset:
- Studio remains responsible for launcher, assets, shipper, debug, build, and run orchestration;
- code-editing architecture is no longer smuggled in through dead UI or legacy session seams.
## Example
Before the cleanup, an old `.studio/state.json` could still restore editor-centric state.
After the cleanup, the same persisted payload is treated defensively:
- old editor fields are ignored;
- the selected workspace falls back safely when `EDITOR` is present;
- the project reopens into a surviving workspace instead of reviving a dead surface.
## Pitfalls and Anti-Patterns
- Do not preserve dead editor-era boundaries "for compatibility" when the product direction has already moved.
- Do not treat naming continuity as architectural continuity. A later `prometeu-lsp` module can be valid if it comes from a new discussion and a new boundary.
- Do not leave project-local state as an accidental migration trap; persisted legacy fields must degrade safely.
## References
- `AGD-0034` Limpeza completa do Workspace Editor do Studio e remoção dos acoplamentos legados
- `DEC-0031` Limpeza completa do Workspace Editor do Studio e remoção dos acoplamentos legados
- `PLN-0062` Shell Session and State Cleanup After Editor Removal
- `PLN-0063` Legacy LSP and VFS Module Removal with Runtime Preservation
- `PLN-0064` Normative Test and Lesson Cleanup for Editor Stack Removal
- `LSN-0047` Project-scoped LSP boundary and protocol containment

View File

@ -0,0 +1,106 @@
---
id: LSN-0047
ticket: studio-new-lsp-api-and-v1-boundary
title: Project-Scoped LSP Boundary and Protocol Containment
created: 2026-05-07
tags: [studio, lsp, vscode, protocol, api, boundary, compiler]
---
## Context
After the legacy embedded editor stack was removed, Prometeu needed a new LSP baseline without repeating the old mistake of collapsing protocol, session lifecycle, host UI, and semantic ownership into one module.
The main architectural pressure came from two facts:
- the VS Code extension already existed as a real LSP client over TCP,
- and the compiler already existed as the canonical owner of semantic behavior.
That meant the missing piece was not "add editor features first". The missing piece was a strict boundary that let the Studio host, the protocol adapter, and the compiler evolve without contaminating each other.
## Key Decisions
### Keep `lsp-api` Minimal and Internal
**What:**
`lsp-api` became a narrow internal Studio boundary, starting with project-scoped lifecycle operations such as boot and shutdown instead of mirroring the LSP protocol.
**Why:**
The Studio needed a reusable internal service boundary, not a second copy of the external wire protocol.
**Trade-offs:**
The API starts intentionally small and may need explicit growth later, but that is safer than locking protocol-shaped DTOs into the internal architecture too early.
### Contain `LSP4J` and Protocol DTOs Inside `lsp-v1`
**What:**
`lsp-v1` became the only concrete protocol adapter and the only module allowed to depend on `LSP4J`.
**Why:**
Protocol libraries are integration details. If they leak outward, the host architecture starts depending on the current transport and implementation framework instead of on stable domain boundaries.
**Trade-offs:**
This forces explicit mapping layers and a little more ceremony, but it keeps protocol churn from infecting the rest of the codebase.
### Make the Server Lifecycle Project-Scoped
**What:**
The LSP server now belongs to project open/close lifecycle instead of global Studio process startup.
**Why:**
Project scope is the real ownership boundary for source roots, compiler context, and editor-facing behavior.
**Trade-offs:**
Lifecycle wiring is slightly more involved, but resource ownership becomes predictable and multi-project behavior stays sane.
### Keep `compiler` as Semantic Owner Even in a Dumb First Wave
**What:**
The baseline server remained intentionally simple in behavior, but its request handling already routes through compiler-facing bridge seams.
**Why:**
A "temporary mock" becomes dangerous when it also becomes the architecture. The repository needed a structurally correct baseline before adding semantic depth.
**Trade-offs:**
Wave 1 delivered less feature richness, but it created a safe foundation for later layering.
## Patterns and Algorithms
### Pattern: Internal Boundary, External Adapter
The stable split is:
1. `lsp-api` exposes internal lifecycle operations,
2. `lsp-v1` speaks protocol and transport,
3. `compiler` owns semantic and analysis behavior,
4. the VS Code extension remains an ordinary external LSP client.
### Pattern: Build the Seams Before the Features
The server can begin "dumb" in capability coverage if:
- the lifecycle boundary is already correct,
- compiler access already flows through explicit bridge services,
- and protocol containment is already enforced.
That sequence is safer than shipping richer features on top of a blurred module boundary.
## Pitfalls
- Do not let `lsp-api` grow into a shadow copy of the LSP protocol.
- Do not import `LSP4J` outside `lsp-v1`, even for convenience.
- Do not move semantic ownership into the protocol adapter just because the adapter is the caller-facing layer.
- Do not boot a global server for the entire Studio process when the actual ownership boundary is the project session.
- Do not treat a connectivity mock as an acceptable long-term module shape.
## References
- `DEC-0032` Boundary normativo entre lsp-api, lsp-v1 e a extensao VS Code
- `PLN-0065` LSP Boundary and Module Scaffolding
- `PLN-0066` Project-Scoped LSP Server Lifecycle in Studio
- `PLN-0067` Compiler-Backed Dumb LSP Server Baseline
## Takeaways
- Protocol adapters should stay narrow and concrete; internal Studio boundaries should stay protocol-agnostic.
- Project-scoped lifecycle is the correct owner for the LSP server.
- A dumb first wave is acceptable only when the architecture is already correct.

View File

@ -0,0 +1,93 @@
---
id: LSN-0050
discussion: DSC-0033
title: Frontend-owned visual themes with structured contract and host adapters
status: done
created: 2026-05-08
updated: 2026-05-08
tags: [compiler, compiler-general, frontend, presentation, theming, lsp, vscode, pbs]
---
## Original Problem
Frontend semantic presentation was frontend-owned in principle, but not in a form that could survive multiple hosts.
PBS still depended on authored semantic CSS as the practical visual source of truth, which created three problems:
- CSS was a host-facing artifact rather than a compiler-general contract;
- VS Code could only approximate frontend intent through manual translation;
- every host risked drifting away from the frontend's real visual identity.
## Consolidated Decision
`DEC-0033` replaced authored host-consumed semantic CSS with a structured frontend-owned visual contract.
That contract now owns:
- the frontend theme list;
- the default active theme;
- token-scoped styles;
- editor-surface palette data;
- and host projections used to translate frontend semantic keys into host-native semantic selectors.
Hosts are adapters over this data.
They do not own frontend visual truth.
## Final Result
The canonical visual source now lives in `FrontendSemanticPresentationSpec`, with concrete PBS theme data and host projections authored in structured Java models instead of CSS resources.
The new LSP path transports:
- frontend semantic keys;
- visual themes;
- active theme identity;
- and host projection metadata.
The VS Code extension consumes that payload and writes semantic token and workbench color customizations dynamically, which means routine frontend color changes no longer require hand-edited extension theme tables.
The old PBS semantic CSS still exists only as migration residue and derivative compatibility material.
It is no longer the authored canonical contract.
## Implementation Notes
The decision was realized across four layers:
1. compiler/frontend metadata grew a structured presentation contract;
2. PBS migrated its theme definition into that contract;
3. the LSP description path started exporting visual themes and host projections;
4. the VS Code extension became a mechanical translator of the contract at runtime.
This also clarified the relationship between `DSC-0033` and `DSC-0034`:
- `DSC-0033` established the structured theme contract and CSS retirement;
- `DSC-0034` established that host projections belong in the frontend contract rather than in the host.
Together they define the current highlight pipeline.
## Example
For PBS today:
- the frontend publishes semantic keys such as `pbs-keyword` and `pbs-service`;
- the same frontend publishes the canonical theme colors and emphasis flags for those keys;
- the LSP sends the theme plus a `vscode` host projection;
- the VS Code extension applies both the canonical semantic selectors and the projected host selectors.
This is why a color change in PBS theme data can flow to VS Code without a new curated palette in `package.json`.
## Pitfalls and Anti-Patterns
- Do not reintroduce CSS as a silent second source of truth.
- Do not move frontend theme authorship into host adapters just because one host has capability gaps.
- Do not collapse frontend semantic identity into generic host categories during transport; host categories are projections, not the canonical vocabulary.
## References
- `AGD-0036` Frontend Visual Theme Spec and Retirement of Host-Consumed Semantic CSS
- `DEC-0033` Frontend-owned visual theme spec as canonical presentation contract
- `PLN-0068` Frontend Visual Contract and PBS Migration
- `PLN-0069` LSP Visual Theme Transport and Theme Selection Propagation
- `PLN-0070` Studio Visual Theme Adapter and CSS Retirement
- `PLN-0071` VS Code Dynamic Theme Translation Adapter
- `LSN-0048` Frontend-owned semantic vocabularies with declarative host projection

View File

@ -0,0 +1,90 @@
---
id: LSN-0048
ticket: frontend-semantic-host-projection-flexibility
title: Frontend-Owned Semantic Vocabularies with Declarative Host Projection
created: 2026-05-07
tags: [compiler, compiler-general, frontend, semantics, vscode, host-projection, lsp]
---
## Context
Prometeu had already established that frontend-owned visual themes should remain canonical.
The remaining semantic risk was subtler: VS Code prefers host-shaped token categories, and that pressure could easily turn the host taxonomy into the real source of semantic truth.
This discussion closed that gap by making semantic ownership explicit:
- the frontend keeps the canonical semantic vocabulary,
- host adaptation is additional metadata,
- and the adapter translates mechanically instead of inventing semantic policy.
## Key Decisions
### Keep Frontend Semantic Keys Canonical
**What:**
Each frontend remains free to define and evolve its own semantic keys, and those keys stay canonical across the stack.
**Why:**
Semantic meaning belongs to the language frontend, not to a single host's preferred category system.
**Trade-offs:**
Adapters must handle more varied vocabularies, but that is the correct cost of preserving frontend ownership.
### Add Declarative Host Projection Instead of Global Normalization
**What:**
The frontend now authors host projection metadata describing how canonical semantic keys should map into VS Code token types, modifiers, and fallbacks.
**Why:**
Hosts still need a practical adaptation path, but that adaptation should be explicit projection data rather than a hidden normalization rule.
**Trade-offs:**
The contract grows an extra layer, but it stays declarative and prevents semantic meaning from being reauthored inside the adapter.
### Keep LSP and the Extension Mechanical
**What:**
LSP transports canonical keys plus projection metadata, and the VS Code extension consumes both mechanically.
**Why:**
If either layer starts hand-mapping semantic meaning, the host stack becomes the de facto author of language semantics.
**Trade-offs:**
The transport and adapter need clearer model shapes, but their responsibilities stay cleaner and more reusable.
## Patterns and Algorithms
### Pattern: Canonical Vocabulary Plus Per-Host Projection
The stable flow is:
1. frontend defines semantic keys,
2. frontend defines host projections for those keys,
3. LSP transports canonical keys and projection data together,
4. the host adapter applies the projection without reinterpretation.
### Pattern: Mechanical Translation with Explicit Fallbacks
Projection metadata should say not only the preferred host token type, but also the fallback when the host cannot represent the frontend key exactly.
That keeps fallback behavior visible and reviewable instead of implicit in adapter code.
## Pitfalls
- Do not collapse frontend semantic keys into a repository-wide host taxonomy.
- Do not hide semantic policy inside the VS Code extension just because the platform has its own token vocabulary.
- Do not transport only host-shaped token types and discard canonical frontend identity.
- Do not confuse projection metadata with the canonical semantic contract itself.
## References
- `DEC-0034` Frontend semantic vocabularies remain canonical and hosts consume declarative projections
- `PLN-0072` Frontend Semantic Host Projection Contract
- `PLN-0073` LSP Semantic Host Projection Transport
- `PLN-0074` VS Code Semantic Host Projection Adapter
- `DEC-0033` Frontend-owned visual theme spec as canonical presentation contract
## Takeaways
- Semantic ownership stays with the frontend even when the host prefers a different taxonomy.
- Host projection is the right adaptation mechanism; global normalization is not.
- LSP and host adapters stay healthier when they translate declaratively instead of authoring semantic meaning.

View File

@ -0,0 +1,207 @@
---
id: AGD-0038
ticket: pbs-lsp-editor-assistance-wave-1
title: Wave 1 de assistencia editorial via LSP para PBS no VS Code
status: accepted
created: 2026-05-08
resolved: 2026-05-08
decision: DEC-0035
tags: [studio, lsp, vscode, compiler, compiler-pbs, editor, completion, hover, signature-help]
---
## Pain
O stack atual de PBS no VS Code ja entrega duas capacidades relevantes:
- diagnostics basicos;
- semantic highlighting com tema frontend-owned.
Mas ele ainda nao entrega o minimo de assistencia editorial para um fluxo real de escrita de codigo:
- nao existe `completion`;
- `hover` ainda eh placeholder;
- nao existe `signature help`.
Sem isso, o editor continua servindo mais como viewer colorido com erros do que como ambiente minimamente utilizavel para escrever PBS com conforto.
## Context
Domain owner: `studio`
Subdomains touched:
- `studio/lsp`
- `compiler/pbs`
- `compiler/general`
Estado atual observado no codigo:
- o servidor anuncia `hover` e `semanticTokens`, mas nao anuncia `completionProvider`, `signatureHelpProvider` ou `documentFormattingProvider`;
- o bridge interno do LSP so expõe `describeServer`, `analyzeDocument`, `hover`, `semanticTokens` e `onSave`;
- `hover` hoje retorna texto fixo, sem usar simbolo, assinatura, tipo ou docs;
- o pipeline do compiler ja consegue analisar overlay do documento e possui superficies semanticas e de linking relevantes no PBS;
- o highlighting recente ja resolve bastante informacao lexical e parte da superficie stdlib/imports.
Esta agenda trata apenas da wave 1 de assistencia editorial.
Ela nao tenta fechar todo o problema de IDE features.
## Open Questions
- Qual deve ser o escopo minimo de `completion` para chamarmos essa wave de utilizavel?
- [x] Qual deve ser o escopo minimo de `completion` para chamarmos essa wave de utilizavel?
R: keywords, nomes locais em escopo, simbolos importados/publicos, e members apos `.` para superfícies reconhecidas (`service`, `host`, `builtin type`, `struct` e constructors/funcoes relacionados). Sem isso, a feature ainda nao ajuda na descoberta real da API.
- `completion` deve nascer apenas com heuristicas lexicais/sintaticas, ou ja deve usar semantica e linking do compiler onde isso for viavel?
- [x] `completion` deve nascer apenas com heuristicas lexicais/sintaticas, ou ja deve usar semantica e linking do compiler onde isso for viavel?
R: deve nascer compiler-backed sempre que a informacao ja existir de forma confiavel no pipeline semantico/linking. Heuristica local pode existir apenas como complemento estritamente limitado para casos ainda nao resolvidos, nunca como segunda semantica canônica.
- Em `hover`, quais informacoes sao obrigatorias na primeira entrega:
assinatura, kind, modulo de origem, tipo inferido, documentacao?
- [x] Em `hover`, quais informacoes sao obrigatorias na primeira entrega:
assinatura, kind, modulo de origem, tipo inferido, documentacao?
R: obrigatorios: `kind`, assinatura quando aplicavel, modulo/origem quando relevante, e tipo/shape basico quando houver. Documentacao textual eh desejavel para stdlib e builtins, mas nao deve bloquear a wave 1 se a infraestrutura de docstring ainda nao existir. Se a linguagem vier a adotar algo como `Document(...)` com markdown pronto para hover, isso deve nascer em discussao propria; nesta wave atual, basta preparar o local de consumo para documentacao opcional quando ela existir.
- `signature help` deve usar a mesma fonte semantica de `hover/completion`, ou pode nascer numa implementacao mais local para chamadas conhecidas?
- [x] `signature help` deve usar a mesma fonte semantica de `hover/completion`, ou pode nascer numa implementacao mais local para chamadas conhecidas?
R: deve usar a mesma base semantica. O objetivo da wave eh justamente evitar tres caminhos tecnicos diferentes para responder "qual assinatura esta sendo chamada?".
- Como tratar membros de stdlib, aliases importados, services, hosts, builtin types e metodos de struct sem criar uma segunda semantica paralela no LSP?
- [x] Como tratar membros de stdlib, aliases importados, services, hosts, builtin types e metodos de struct sem criar uma segunda semantica paralela no LSP?
R: o LSP deve perguntar ao compiler por uma superficie editorial/resolutiva unica, derivada das mesmas fontes usadas para analise, linking, stdlib e metadata do frontend. O LSP nao deve reconstruir manualmente tabelas paralelas de membros.
- Esta wave deve incluir `go to definition`, ou isso deve ficar explicitamente fora do escopo para nao diluir a entrega?
- [x] Esta wave deve incluir `go to definition`, ou isso deve ficar explicitamente fora do escopo para nao diluir a entrega?
R: deve ficar fora do escopo desta wave. Ele eh um proximo passo natural, mas nao eh requisito para chamar `completion + hover + signature help` de minimamente utilizaveis.
- Formatacao e identacao automatica devem ficar fora desta agenda como non-goal explicito?
- [x] Formatacao e identacao automatica devem ficar fora desta agenda como non-goal explicito?
R: sim. Podem virar discussao/plan posterior. Nesta wave, elas seriam custo adicional sem impacto proporcional comparado a completion/hover/signature help.
## Options
### Option A - Completion e hover por heuristica leve, com signature help posterior
- **Approach:** implementar `completion` usando contexto local, imports, keywords e alguns casos de member access, implementar `hover` com informacao minima, e deixar `signature help` para uma wave seguinte.
- **Pro:** menor custo inicial e menor risco de travar na arquitetura semantica.
- **Con:** pode criar uma primeira entrega inconsistente, especialmente em chamadas de stdlib e tipos importados, e pode forcar refactor cedo demais depois.
- **Maintainability:** media.
### Option B - Wave 1 coerente com completion, hover e signature help sobre uma mesma base semantica
- **Approach:** desenhar uma pequena superficie de assistencia editorial no LSP que use uma base comum para resolver simbolos, membros e assinaturas. Entregar juntos:
- completion minimo utilizavel;
- hover real;
- signature help em chamadas.
- **Pro:** evita duplicacao conceitual e produz uma experiencia de editor mais consistente desde a primeira wave.
- **Con:** custo maior de desenho e implementacao agora.
- **Maintainability:** forte.
### Option C - Adiar assistencia semantica e depender do VS Code + snippets
- **Approach:** manter diagnostics e colors como estao, adicionar no maximo snippets ou pequenas ajudas na extensao.
- **Pro:** quase nenhum custo de backend.
- **Con:** nao resolve o problema real; o editor continua insuficiente para escrita cotidiana de PBS.
- **Maintainability:** fraca.
## Tradeoffs
Option C nao fecha a dor real e deve ser descartada.
Option A parece atraente para ganhar velocidade, mas carrega um risco ruim:
- introduzir `completion`, `hover` e depois `signature help` por caminhos tecnicos diferentes;
- duplicar logica entre parser local, heuristicas e linking;
- e transformar a wave 2 num retrabalho de consolidacao.
Option B custa mais, mas o custo eh justificavel porque estas tres features se apoiam nas mesmas perguntas:
- o que este simbolo representa?
- quais membros ele expõe?
- qual eh a assinatura desta chamada?
- qual o modulo/origem deste item?
Se essas perguntas forem respondidas por uma base comum, a wave 1 fica mais coerente e abre caminho natural para futuras features como `definition`, `references` e `inlay hints` via LSP.
## Recommendation
Seguir com **Option B**.
A recomendacao eh abrir uma wave 1 coerente de assistencia editorial com:
- `completion` minimo utilizavel;
- `hover` funcional;
- `signature help` funcional.
Escopo minimo recomendado para `completion`:
- keywords;
- simbolos locais em escopo;
- nomes importados/publicos relevantes;
- members apos `.` para stdlib, builtin types, structs e services/hosts reconhecidos.
Escopo minimo recomendado para `hover`:
- kind do simbolo;
- assinatura quando aplicavel;
- modulo/origem quando relevante;
- tipo ou shape basico quando isso existir de forma confiavel.
Escopo minimo recomendado para `signature help`:
- chamadas de funcao/metodo/construtor;
- parametro ativo;
- labels consistentes com a mesma representacao de assinatura usada no hover.
## Discussion
O ponto principal desta agenda nao eh “adicionar IDE features” genericamente.
O ponto principal eh definir a primeira wave de superfícies de assistencia que tiram o PBS no VS Code do estado de:
- diagnostics + coloracao
para um estado de:
- escrita assistida minima, com descoberta de API e leitura de assinatura.
Isso exige disciplina de boundary.
O VS Code extension nao deve inventar semantica local.
O servidor LSP deve continuar sendo o owner da assistencia editorial.
Da mesma forma, o LSP nao deve criar uma semantica paralela desconectada do compiler se ja existe informacao aproveitavel em parser, linking, stdlib e superficies semanticas do PBS.
Tambem vale explicitar o que fica fora desta agenda:
- formatter completo;
- identacao automatica robusta de documento;
- quick fixes;
- definition/references;
- rename;
- code actions mais amplas.
Esses itens podem virar agenda posterior.
Tambem fica registrado um criterio importante de arquitetura:
- o primeiro passo nao eh encher o protocolo de features;
- o primeiro passo eh criar uma base editorial coerente no lado `compiler/pbs` + `studio/lsp` que consiga responder simbolo, members e assinatura;
- `completion`, `hover` e `signature help` sao projeções diferentes sobre a mesma base.
E um criterio adicional de preparacao editorial:
- a wave 1 deve prever um campo/superficie opcional de documentacao no modelo editorial resolvido por simbolo;
- mas a autoria dessa documentacao na linguagem PBS, inclusive eventual anotacao `Document(...)` com markdown, fica fora desta agenda e pode seguir para discussao separada.
## Resolution
Resolucao preliminar:
- a wave 1 deve ser considerada valida quando o VS Code conseguir oferecer descoberta minima de API, leitura de assinatura e ajuda em chamadas sem depender de conhecimento local na extensao;
- `completion`, `hover` e `signature help` devem nascer sobre uma mesma base compiler-backed;
- `go to definition`, formatter e outras features de IDE ficam explicitamente fora do escopo.
Se esta leitura fizer sentido, o proximo passo eh aceitar a agenda e fechar uma `decision` que:
1. defina o escopo normativo exato de `completion`, `hover` e `signature help`;
2. fixe o boundary entre `compiler/pbs`, `studio/lsp` e `tools/vscode-extension`;
3. declare explicitamente os non-goals desta wave;
4. determine se a base deve nascer compiler-backed desde o inicio ou com alguma heuristica local estritamente limitada.
## Next Step
Se voce concordar com o enquadramento, eu converto esta agenda em `accepted` e escrevo a `decision` normativa da wave 1 de assistencia editorial para PBS no VS Code.

View File

@ -0,0 +1,137 @@
---
id: AGD-0039
ticket: pbs-symbol-documentation-and-hover-markdown
title: Modelo de documentacao de simbolos em PBS e consumo markdown no hover
status: open
created: 2026-05-08
tags: [compiler, compiler-pbs, studio, lsp, vscode, editor, hover, documentation, markdown]
---
## Pain
A wave atual de assistencia editorial quer `hover` util, mas ainda nao existe um modelo claro para documentacao authored dentro de PBS.
Sem esse modelo, o hover pode até mostrar:
- kind;
- assinatura;
- origem;
- tipo/shape.
Mas continua sem uma superficie limpa para explicar intencao de uso, contrato semantico, detalhes de parametros ou observacoes de API.
Hoje a ideia proposta eh algo como:
- `Document(qualquer markdown aqui dentro...)`
como anotacao consumida diretamente pelo hover.
Isso parece simples no uso, mas toca em decisoes de linguagem e contrato editorial que merecem discussao propria.
## Context
Domain owner: `compiler/pbs`
Subdomains touched:
- `compiler/general`
- `studio/lsp`
- `tools/vscode-extension`
Esta agenda eh deliberadamente separada de `AGD-0038`.
Razao:
- `AGD-0038` trata da wave 1 de `completion`, `hover` e `signature help`;
- esta nova agenda trata da autoria, armazenamento, propagacao e consumo de documentacao de simbolos.
O objetivo eh evitar que a primeira wave de assistencia editorial fique bloqueada por uma decisao de linguagem ainda nao fechada.
## Open Questions
- A autoria da documentacao deve acontecer por anotacao explicita como `Document(...)`, por comentario estruturado, ou por outro mecanismo?
- O payload authored deve aceitar markdown arbitrario como fonte canonica, ou deve haver alguma normalizacao/restricao?
- Quais simbolos podem carregar documentacao na wave inicial:
funcoes, methods, services, hosts, builtin types, structs, enums, fields, constructors?
- A documentacao deve viver no AST/semantics como superficie compiler-backed explicita?
- Como a documentacao authored em PBS se relaciona com documentacao de stdlib gerada ou importada de outras fontes?
- O hover deve consumir markdown pronto sem transformacao, ou deve haver um pipeline intermediario de sanitizacao/normalizacao?
- Como tratar multilinhas, escaping e ergonomia de escrita se a surface for `Document(...)`?
- A agenda deve definir apenas o modelo de autoria, ou tambem o transporte LSP e a composicao final do hover?
## Options
### Option A - Anotacao `Document(...)` com markdown como payload canonico
- **Approach:** introduzir uma anotacao explicita na linguagem PBS cujo conteudo ja eh markdown pronto para o hover.
- **Pro:** autoria direta, intencao clara, pipeline simples de consumo no LSP e no hover.
- **Con:** precisa desenhar sintaxe, escaping, multiline e regras de onde a anotacao eh permitida.
- **Maintainability:** forte se a surface de linguagem ficar limpa.
### Option B - Comentarios estruturados como fonte de documentacao
- **Approach:** usar comentarios especiais ou doc-comments, com parser especifico para gerar documentacao de simbolo.
- **Pro:** mais familiar para quem vem de outras linguagens e menos intrusivo no corpo semantico da linguagem.
- **Con:** exige novo contrato de binding entre comentarios e simbolos, e pode aumentar ambiguidade editorial.
- **Maintainability:** media.
### Option C - Nao definir autoria em PBS agora e suportar apenas docs de stdlib por metadata externa
- **Approach:** deixar a linguagem sem surface authored por enquanto e alimentar hover apenas com docs externas/geradas.
- **Pro:** menor custo imediato de linguagem.
- **Con:** resolve mal a autoria de codigo de usuario e deixa o modelo incompleto.
- **Maintainability:** fraca.
## Tradeoffs
Option C nao parece suficiente como direcao de produto se a intencao eh ter hover realmente util para APIs authored em PBS.
Option B eh plausivel, mas move o problema para um binding entre comentario e simbolo que tambem precisa ser projetado com cuidado. Isso costuma parecer mais barato do que realmente eh.
Option A tem um custo claro de sintaxe e modelo, mas pelo menos deixa explicito que documentacao eh metadado semantico e nao apenas texto solto ao redor do codigo.
Se a equipe realmente quer markdown authored pronto para consumo no hover, Option A parece a discussao mais direta e honesta.
## Recommendation
Discutir esta agenda com viés inicial para **Option A**, mas sem fechar ainda a sintaxe final.
A recomendacao preliminar eh separar o problema em duas perguntas:
1. qual eh a surface canonical de autoria da documentacao em PBS?
2. como essa documentacao vira um payload editorial resolvido para hover e futuros consumers?
Mesmo que `Document(...)` seja a direcao vencedora, a decisao deve deixar claro:
- onde ela pode ser usada;
- qual formato textual aceita;
- como ela eh preservada no compiler;
- como chega ao LSP;
- e como o hover deve compor isso com assinatura e metadados do simbolo.
## Discussion
Esta agenda nao deve ser reduzida a "mostrar markdown no hover".
O problema real eh definir ownership e boundary da documentacao:
- o author escreve onde?
- o compiler preserva como?
- o LSP transporta em qual shape?
- o hover monta o resultado com qual ordem e responsabilidade?
Tambem vale observar que a escolha aqui pode influenciar mais do que hover:
- completions futuras podem querer `detail` e `documentation`;
- signature help pode querer descricoes de parametros;
- docs de stdlib podem querer surface unificada com docs authored.
Por isso, mesmo sendo uma agenda separada, ela conversa diretamente com a futura qualidade do editor.
## Resolution
Ainda em aberto.
## Next Step
Se o enquadramento fizer sentido, o proximo passo eh discutir as open questions e fechar uma `decision` propria para o modelo de documentacao de simbolos em PBS.

View File

@ -0,0 +1,192 @@
---
id: DEC-0035
ticket: pbs-lsp-editor-assistance-wave-1
title: Wave 1 de assistencia editorial via LSP para PBS no VS Code
status: accepted
created: 2026-05-08
accepted: 2026-05-08
agenda: AGD-0038
plans: [PLN-0075, PLN-0076, PLN-0077]
tags: [studio, lsp, vscode, compiler, compiler-pbs, editor, completion, hover, signature-help]
---
## Context
O stack atual de PBS no VS Code ja entrega diagnostics basicos e semantic highlighting frontend-owned, mas ainda nao oferece assistencia editorial minima para escrita cotidiana de codigo.
Hoje:
1. nao existe `completion`;
2. `hover` existe apenas como placeholder;
3. nao existe `signature help`;
4. o servidor ainda nao anuncia capabilities correspondentes para essas superficies;
5. o compiler e o frontend PBS ja possuem informacao semantica, de linking, stdlib e metadata suficiente para nao precisarmos inventar uma segunda semantica local na extensao.
Esta decisao trata apenas da primeira wave de assistencia editorial para PBS no VS Code.
Ela nao cobre formatter, identacao automatica robusta, quick fixes, `go to definition`, `references`, `rename` nem code actions gerais.
## Decision
O repositório SHALL entregar uma wave 1 de assistencia editorial via LSP para PBS no VS Code com tres superfícies funcionais:
1. `completion`;
2. `hover`;
3. `signature help`.
Esta wave SHALL ser considerada valida quando essas tres superfícies permitirem:
1. descoberta minima de API;
2. leitura de assinatura em simbolos e chamadas;
3. ajuda de chamada sem dependencia de conhecimento semantico local na extensao VS Code.
Esta decisao ALSO locks que:
1. `completion`, `hover` e `signature help` MUST nascer sobre uma mesma base editorial compiler-backed;
2. o LSP MUST ser o owner operacional da assistencia editorial;
3. a extensao VS Code MUST atuar apenas como cliente/adaptador do protocolo, e MUST NOT reconstruir semantica propria para simbolos, members ou assinaturas;
4. heuristicas locais no LSP MAY existir apenas como complemento temporario e estritamente limitado para lacunas nao resolvidas, mas MUST NOT virar uma segunda semantica canônica;
5. `go to definition`, formatter, identacao automatica robusta, quick fixes, `references`, `rename` e code actions amplas MUST ficar fora desta wave.
## Rationale
As tres features desta wave respondem essencialmente ao mesmo conjunto de perguntas:
1. que simbolo eh este?
2. quais members ele expõe?
3. qual assinatura ele tem?
4. de onde ele vem?
Implementa-las por caminhos tecnicos diferentes introduziria:
1. duplicacao entre parser local, heuristicas e linking;
2. incoerencia entre o que aparece em `completion`, `hover` e `signature help`;
3. retrabalho na wave seguinte.
Portanto, a primeira entrega precisa nascer sobre uma base editorial unificada, reaproveitando o que o compiler e o frontend PBS ja sabem sobre:
1. escopo local;
2. imports e aliases;
3. stdlib;
4. builtin types;
5. services, hosts, structs, enums, constructors, functions e methods.
## Technical Specification
### 1. Shared Editorial Resolution Surface
`compiler/pbs` e `studio/lsp` MUST introduzir uma superficie editorial resolvida comum para uso de `completion`, `hover` e `signature help`.
Essa superficie MUST ser capaz de responder, de forma compiler-backed:
1. identidade do simbolo;
2. `kind` do simbolo;
3. modulo/origem quando relevante;
4. assinatura ou shape quando aplicavel;
5. members disponíveis para receivers reconhecidos;
6. documentacao opcional quando existir, sem bloquear esta wave.
Essa superficie MAY ser exposta por novos modelos internos do compiler, novos adapters no bridge do LSP, ou ambos.
Mas ela MUST permanecer derivada do compiler e MUST NOT ser reautoriada na extensao.
### 2. Completion Scope
`completion` MUST ser entregue como superfície minima utilizavel.
Na wave 1, isso MUST cobrir pelo menos:
1. keywords da linguagem;
2. nomes locais em escopo;
3. simbolos importados/publicos relevantes;
4. members apos `.` para superfícies reconhecidas, incluindo `service`, `host`, `builtin type`, `struct`, e call surfaces relacionadas.
`completion` SHOULD expor labels, kinds e detalhes coerentes com a mesma base usada por `hover`.
`completion` MUST NOT depender de tabela hardcoded na extensao para stdlib ou members de PBS.
### 3. Hover Scope
`hover` MUST deixar de ser placeholder e passar a ser simbolo-resolved.
Na wave 1, o hover MUST incluir quando disponivel:
1. `kind` do simbolo;
2. assinatura quando aplicavel;
3. modulo/origem quando relevante;
4. tipo ou shape basico quando isso existir de forma confiavel.
`hover` MAY incluir documentacao textual opcional.
Mas a autoria de documentacao em PBS, incluindo eventual anotacao como `Document(...)`, MUST seguir discussao separada e MUST NOT bloquear esta wave.
### 4. Signature Help Scope
`signature help` MUST ser implementado para chamadas de:
1. funcao;
2. metodo;
3. construtor.
Na wave 1, ele MUST informar:
1. a assinatura ativa;
2. o parametro ativo;
3. labels coerentes com a mesma representacao de assinatura usada no hover.
`signature help` MUST usar a mesma base editorial de resolucao usada por `completion` e `hover`.
### 5. Boundary Responsibilities
`compiler/pbs`:
1. MUST continuar sendo a fonte de verdade de semantica, linking e metadata relevante para simbolos e call surfaces;
2. MUST fornecer a base necessaria para resolucao editorial de simbolos, members e assinaturas.
`studio/lsp`:
1. MUST projetar essa base editorial em endpoints/protocol features adequados;
2. MUST anunciar as capabilities LSP necessarias para `completion`, `hover` e `signature help`;
3. MUST manter o transporte fiel ao modelo compiler-backed.
`tools/vscode-extension`:
1. MUST consumir o protocolo como cliente;
2. MUST NOT carregar tabelas paralelas de membros, stdlib ou assinaturas como fonte principal;
3. MAY conter apenas a logica estrutural normal de integração exigida pelo VS Code.
### 6. Documentation Preparation Rule
Esta wave MUST preparar um campo ou superfície opcional para documentacao editorial resolvida por simbolo.
Mas esta decisao ALSO locks que:
1. a surface de autoria dessa documentacao em PBS nao faz parte desta wave;
2. a sintaxe ou contrato de linguagem para documentacao authored deve ser discutido separadamente;
3. a ausencia dessa autoria nao bloqueia `completion`, `hover` nem `signature help` da wave 1.
### 7. Explicit Non-Goals
Esta decisao MUST NOT:
1. incluir formatter completo;
2. incluir identacao automatica robusta de documento;
3. incluir `go to definition`;
4. incluir `references`;
5. incluir `rename`;
6. incluir quick fixes;
7. incluir code actions mais amplas;
8. definir o modelo de autoria de documentacao em PBS.
## Constraints
1. A wave 1 MUST priorizar coerencia entre `completion`, `hover` e `signature help` sobre cobertura enciclopedica de todos os casos.
2. A extensao VS Code MUST permanecer thin; a inteligencia editorial MUST ficar no compiler/LSP.
3. A implementacao MUST favorecer evolucao futura para `definition`, `references`, `documentation`, e outras editor features sem exigir reescrita conceitual da base editorial.
4. A implementacao MUST evitar surfaces duplicadas para stdlib, aliases, members e signatures.
5. Qualquer plan derivado desta decisao MUST separar claramente:
- base editorial compiler-backed;
- projeção LSP/protocolo;
- integração VS Code.
## Revision Log
- 2026-05-08: Initial draft from AGD-0038.
- 2026-05-08: Accepted and decomposed into PLN-0075, PLN-0076, and PLN-0077.

View File

@ -0,0 +1,135 @@
---
id: PLN-0075
ticket: pbs-lsp-editor-assistance-wave-1
title: PBS Editorial Resolution Surface for Symbols, Members, and Signatures
status: open
created: 2026-05-08
completed:
tags: [compiler, compiler-pbs, editor, completion, hover, signature-help, semantics]
---
## Objective
Introduce a compiler-backed editorial resolution surface in `compiler/pbs` that can answer symbol identity, kind, origin, signature/shape, callable details, and receiver members for use by LSP `completion`, `hover`, and `signature help`.
## Background
`DEC-0035` requires all three editor-assistance features to be built over one shared compiler-backed base. The current repository already has useful pieces:
- parser and AST for PBS;
- semantic validation and flow analysis;
- stdlib and import visibility rules;
- semantic token classification;
- builder-pipeline overlay analysis.
What is still missing is an explicit editorial surface designed for interactive symbol resolution instead of only diagnostics or coloring.
## Scope
### Included
- Define internal compiler-side models for editorial symbol resolution.
- Resolve local names, imported/public symbols, and recognized member surfaces.
- Resolve callable signatures for functions, methods, and constructors.
- Provide optional documentation slot(s) in the model without defining authored PBS documentation syntax.
- Add PBS-focused tests for symbol/member/signature resolution.
### Excluded
- LSP protocol capability changes.
- VS Code client behavior.
- Authored documentation syntax such as `Document(...)`.
- `go to definition`, `references`, `rename`, formatter, or code actions.
## Non-Goals
- Rebuilding the full compiler around IDE concerns.
- Solving every ambiguous or partial parse state in wave 1.
- Designing a generic multi-language IDE framework before PBS proves the model.
## Execution Steps
### Step 1 - Define editorial resolution models in compiler-owned surfaces
**What:** Introduce explicit models for resolved symbols, member candidates, callable signatures, and optional documentation payload.
**How:** Add compiler-owned DTO/model types that can express:
1. symbol identity and kind;
2. declared/imported origin;
3. callable label/signature shape;
4. receiver member entries;
5. optional documentation text field.
The model must be expressive enough for `completion`, `hover`, and `signature help` without forcing each consumer to reinterpret raw AST nodes.
**File(s):** `prometeu-compiler/prometeu-compiler-core/src/main/java/**` and/or `prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/**` depending on final ownership split.
### Step 2 - Build PBS symbol resolution for local and imported surfaces
**What:** Resolve names in editorial contexts.
**How:** Reuse parser, name/linking, stdlib import knowledge, and any existing semantic tables to resolve:
1. local bindings in scope;
2. declared top-level PBS symbols;
3. imported/public symbols and aliases;
4. stdlib-owned surfaces currently recognized by the frontend.
The implementation must avoid duplicating semantic truth already present in compiler/linking logic.
**File(s):** `prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/**`.
### Step 3 - Build member and callable resolution
**What:** Resolve members and callable signatures for recognized receiver and call surfaces.
**How:** Support at minimum:
1. member lookup after `.` for services, hosts, builtin types, and structs;
2. direct callable lookup for functions and constructors;
3. active-call signature resolution for later `signature help` use.
This step must produce stable labels/details that can be reused unchanged by LSP features.
**File(s):** `prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/**`.
### Step 4 - Add editorial conformance tests
**What:** Lock the compiler-backed editorial contract before LSP integration.
**How:** Add tests for:
1. local variable and parameter resolution;
2. imported service/host/builtin-type resolution;
3. member lookup after `.` on recognized surfaces;
4. callable signature extraction for function/method/constructor cases;
5. optional documentation field stability when absent.
**File(s):** PBS/compiler test suites under `prometeu-compiler/**/src/test/java/**`.
## Test Requirements
### Unit Tests
- Model invariants for symbol/member/signature payloads.
- Resolver tests covering locals, imports, aliases, members, and constructors.
### Integration Tests
- Compiler-backed overlay/editorial tests using representative PBS snippets and stdlib imports.
### Manual Verification
- Inspect resolved payloads in tests or debug output and confirm they are sufficient to drive all three editor features without ad hoc reinterpretation.
## Acceptance Criteria
- [ ] Compiler-side editorial models exist for symbols, members, signatures, and optional documentation.
- [ ] PBS can resolve local names, imported/public symbols, and recognized member surfaces.
- [ ] Function, method, and constructor signatures are available through the shared editorial surface.
- [ ] No consumer is required to reconstruct signature/member meaning from raw AST-only data.
## Dependencies
- `DEC-0035` accepted and normatively locked.
- Existing PBS semantic and import/linking infrastructure remains the truth source.
## Risks
- Overfitting the first resolver to the current stdlib shape may hurt future fronts or future PBS growth.
- Mixing diagnostics-only semantics with editorial semantics carelessly may produce partial or contradictory answers.
- Incomplete member resolution can make `completion` appear flaky even if `hover` works.

View File

@ -0,0 +1,121 @@
---
id: PLN-0076
ticket: pbs-lsp-editor-assistance-wave-1
title: LSP Completion, Hover, and Signature Help over Editorial Resolution
status: open
created: 2026-05-08
completed:
tags: [studio, lsp, protocol, completion, hover, signature-help, editor]
---
## Objective
Expose `completion`, real `hover`, and `signature help` through the project-scoped LSP stack by projecting the compiler-backed editorial resolution surface into explicit protocol capabilities and request handlers.
## Background
`DEC-0035` requires `studio/lsp` to be the operational owner of these editor features while remaining faithful to compiler-backed symbol, member, and signature resolution. The current LSP path only handles diagnostics, semantic tokens, and a placeholder hover.
## Scope
### Included
- Extend the internal bridge/API with completion and signature-help operations.
- Replace placeholder hover with symbol-resolved hover.
- Announce LSP capabilities for completion and signature help.
- Add message models/mappers/tests for the new request/response paths.
### Excluded
- Creation of the underlying compiler editorial model itself.
- Authored documentation syntax.
- VS Code-specific UI behavior beyond normal LSP consumption.
## Non-Goals
- `go to definition`, `references`, `rename`, formatter, code actions, or quick fixes.
- Rich workspace-wide indexing beyond what the shared editorial surface already provides.
## Execution Steps
### Step 1 - Extend the LSP bridge contract
**What:** Add compiler-backed editor-assistance operations to the bridge.
**How:** Evolve `LanguageServiceBridge` and related internal messages to support at minimum:
1. completion requests;
2. symbol-resolved hover payloads;
3. signature-help requests.
The bridge contract must align structurally with the shared editorial resolution surface rather than inventing a second feature-specific abstraction.
**File(s):** `prometeu-lsp/prometeu-lsp-v1/src/main/java/p/studio/lsp/services/**`, `prometeu-lsp/prometeu-lsp-v1/src/main/java/p/studio/lsp/messages/**`.
### Step 2 - Implement compiler-backed request handling
**What:** Wire LSP requests to compiler-backed editorial resolution.
**How:** Update `CompilerLanguageServiceBridge` so it:
1. resolves completion candidates from local/import/member contexts;
2. resolves hover payloads from symbol identity, origin, and signatures;
3. resolves active call signatures for `signature help`.
This step must remove the current fixed hover placeholder.
**File(s):** `prometeu-lsp/prometeu-lsp-v1/src/main/java/p/studio/lsp/services/compiler/**`.
### Step 3 - Announce and map protocol capabilities
**What:** Make the server advertise the new feature set and map payloads into lsp4j types.
**How:** Update protocol mappers and text-document service so the server announces:
1. `completionProvider`;
2. `signatureHelpProvider`;
3. improved `hoverProvider` backed by real payloads.
Map completion items, hover markdown, and signature-help structures into concrete LSP responses.
**File(s):** `prometeu-lsp/prometeu-lsp-v1/src/main/java/p/studio/lsp/services/protocol/**`, `.../mapping/**`.
### Step 4 - Add transport and conformance tests
**What:** Prevent regression to placeholder/editor-local behavior.
**How:** Add tests that verify:
1. capabilities are announced correctly;
2. completion returns compiler-backed candidates;
3. hover no longer returns the baseline placeholder;
4. signature-help returns deterministic active-signature information.
**File(s):** `prometeu-lsp/prometeu-lsp-v1/src/test/java/**`.
## Test Requirements
### Unit Tests
- DTO/model invariants for completion, hover, and signature-help payloads.
- Mapper tests for lsp4j conversion.
### Integration Tests
- LSP request/response tests using realistic PBS snippets with locals, imports, and member access.
### Manual Verification
- Connect the VS Code extension and confirm server capabilities include completion and signature help.
- Confirm hover content changes with the hovered symbol rather than returning static baseline text.
## Acceptance Criteria
- [ ] The LSP bridge exposes completion and signature-help operations in addition to diagnostics, hover, and semantic tokens.
- [ ] Hover is symbol-resolved and no longer hardcoded placeholder text.
- [ ] The server advertises completion and signature-help capabilities.
- [ ] Completion, hover, and signature help all derive from the same compiler-backed editorial surface.
## Dependencies
- `DEC-0035` accepted and normatively locked.
- `PLN-0075` for the shared compiler-backed editorial resolution surface.
## Risks
- Protocol mapping can drift from compiler payload shape if feature-specific shortcuts are added.
- Active parameter/signature calculation may be brittle around incomplete syntax unless request handling is conservative.
- Completion can feel noisy if candidate ranking or filtering is too weak in wave 1.

View File

@ -0,0 +1,103 @@
---
id: PLN-0077
ticket: pbs-lsp-editor-assistance-wave-1
title: VS Code Consumption and Wave 1 Editor Assistance Validation
status: open
created: 2026-05-08
completed:
tags: [vscode, extension, editor, completion, hover, signature-help, validation]
---
## Objective
Keep the VS Code extension thin while validating that the new LSP-delivered `completion`, `hover`, and `signature help` features actually produce a minimally usable PBS editing experience end to end.
## Background
`DEC-0035` explicitly requires the extension to remain a client/adaptor rather than a second semantic engine. Because VS Code already knows how to consume LSP completion, hover, and signature-help responses, the extension work should be small and mostly about ensuring transport, configuration, and real validation are correct.
## Scope
### Included
- Confirm the extension does not need or gain local semantic tables for stdlib, members, or signatures.
- Adjust only the minimal extension/bootstrap behavior needed for the new LSP features to flow correctly.
- Add verification coverage and manual validation guidance for the full wave.
### Excluded
- Compiler-side editorial resolution logic.
- LSP-side protocol feature implementation.
- Formatter/indentation, code actions, or non-wave-1 IDE features.
## Non-Goals
- Turning the extension into a feature-rich semantic client.
- Adding hardcoded completion/signature content for PBS inside TypeScript runtime code.
## Execution Steps
### Step 1 - Verify thin-client integration boundaries
**What:** Confirm the extension remains a pure LSP consumer for the new features.
**How:** Review and adjust the extension only where necessary so completion, hover, and signature help are obtained from the LSP session without client-side semantic fallback tables.
**File(s):** `tools/vscode-extension/src/**`, `tools/vscode-extension/package.json`.
### Step 2 - Add any minimal structural configuration required by VS Code
**What:** Keep static contribution surfaces minimal and structural.
**How:** If VS Code requires additional structural contribution metadata for the new features, add only that metadata. Do not add curated PBS semantic or signature data into the extension package.
**File(s):** `tools/vscode-extension/package.json`.
### Step 3 - Add validation coverage for end-to-end editor assistance
**What:** Lock the no-local-semantics rule and the end-to-end feature expectations.
**How:** Add tests or deterministic verification steps that ensure:
1. the extension connects and receives the feature set from the server;
2. no local completion/signature tables were introduced;
3. the user-facing wave can be exercised end to end.
**File(s):** `tools/vscode-extension/**`, existing validation harnesses if present, and related docs/test notes.
### Step 4 - Define manual smoke scenarios for wave 1 usability
**What:** Make the wave verifiable as a product experience rather than only a protocol exercise.
**How:** Document and run manual checks for:
1. keyword and local-name completion;
2. imported stdlib/member completion after `.`;
3. hover on locals, imported services, builtin types, and methods;
4. signature help during function/method/constructor calls.
**File(s):** extension verification notes or adjacent implementation docs/tests as appropriate.
## Test Requirements
### Unit Tests
- Any extension-side helpers introduced for structural integration only.
### Integration Tests
- End-to-end or near-end-to-end validation that VS Code consumes the server feature set without local semantic duplication.
### Manual Verification
- Open representative PBS files in VS Code and verify `completion`, `hover`, and `signature help` behavior matches the expected wave-1 scope.
## Acceptance Criteria
- [ ] The extension remains thin and contains no new hardcoded PBS semantic tables for members or signatures.
- [ ] VS Code can consume the new LSP completion, hover, and signature-help responses without extension-side semantic authorship.
- [ ] Wave-1 smoke scenarios are documented and manually verifiable.
## Dependencies
- `DEC-0035` accepted and normatively locked.
- `PLN-0076` for the actual LSP feature delivery.
## Risks
- It is easy to accidentally patch around server gaps with client-local logic; this plan must resist that.
- Validation can become too protocol-centric and miss real usability failures if manual smoke scenarios are weak.

View File

@ -44,15 +44,10 @@ The left workspace selector remains fixed in the baseline Studio shell.
The baseline workspace set includes:
- Code Editor;
- Asset Management;
- Debugger/Profiler;
- Shipper, temporarily represented by the current `BuilderWorkspace`.
Workspace-local behavior for the first `Code Editor` wave is defined in:
- `5. Code Editor Workspace Specification.md`
`BuilderWorkspace` is transitional and must not be treated as a long-term architectural reference for the final shipping surface.
## Workspace Architecture Model
@ -71,7 +66,7 @@ Baseline workspace architecture rules are:
The shell must not encourage a workspace model where every interaction falls back to whole-workspace refresh.
Detailed local workspace composition remains workspace-owned.
For example, `Code Editor`-local layout, navigator behavior, tab rules, passive placeholder surfaces, document access-mode behavior, and editor-local save surfaces are not shell-global rules and must be defined by the workspace-local editor specification.
For example, `Assets`-local navigator behavior, detail-pane composition, and mutation flows are not shell-global rules and must be defined by the workspace-local specifications that still exist.
Project-session boundaries may still exist above a workspace when the state must remain alive while the project window stays open.

View File

@ -1,377 +0,0 @@
# Code Editor Workspace Specification
## Status
Active
## Applies To
- `prometeu-studio`
- the Studio `Code Editor` workspace
- the first controlled editor write wave for supported documents including frontend sources
## Purpose
Define the normative Studio contract for the first `Code Editor` workspace wave.
This specification stabilizes:
- the baseline visual composition of the workspace,
- the `Project Navigator` role and scope,
- the controlled editable file-opening model,
- the responsive tab baseline,
- the editor-owned composition surfaces that host save and semantic-read UX,
- the gutter-based active-structure indicator model for semantic editor scopes,
- the editor-local save surfaces,
- and the workspace boundary with the integrated frontend semantic-read phase.
## Authority and Precedence
This specification extends:
- [`1. Studio Shell and Workspace Layout Specification.md`](1.%20Studio%20Shell%20and%20Workspace%20Layout%20Specification.md)
- [`2. Studio UI Foundations Specification.md`](2.%20Studio%20UI%20Foundations%20Specification.md)
- [`3. Studio Components Module Specification.md`](3.%20Studio%20Components%20Module%20Specification.md)
- [`6. Project Document VFS Specification.md`](6.%20Project%20Document%20VFS%20Specification.md)
- [`7. Integrated LSP Semantic Read Phase Specification.md`](7.%20Integrated%20LSP%20Semantic%20Read%20Phase%20Specification.md)
If this document conflicts with the global Studio shell specifications, the shell specifications control shell-wide behavior and this document controls workspace-local editor behavior.
## Normative Inputs
The `Code Editor` workspace must assume:
- the Studio shell already mounts `Code Editor` as a baseline workspace,
- the current wave allows editing for the supported document classes classified by `prometeu-vfs` as editable, including frontend-scoped supported documents,
- all project files remain visible in the editor workspace even when only some are frontend-relevant,
- `prometeu.json` plus the selected frontend may identify source roots worth tagging,
- the integrated frontend semantic-read phase may add diagnostics, symbols, outline-facing structure, definition, and highlight for frontend documents,
- the integrated frontend semantic-read phase may later add dedicated structural-anchor metadata for exact scope-indicator anchoring,
- frontend semantic presentation contract data comes from frontend-owned metadata published through `FrontendSpec`,
- and the current wave must not depend on semantic behavior to remain coherent.
This workspace may consume Studio project metadata and Studio-owned workspace framework primitives, but it must not infer a semantic-provider contract beyond the explicit frontend-owned descriptor surfaced by the integrated semantic-read phase.
This workspace must consume project tree and document data through `prometeu-vfs` rather than through editor-owned direct filesystem readers.
This workspace must consume document access policy and save capability from `prometeu-vfs` rather than rederiving editability locally.
## Workspace Model
The `Code Editor` workspace is:
- project-aware,
- file-oriented,
- controlled-editable in this wave, with supported documents following the canonical access mode provided by `prometeu-vfs`,
- and not a full semantic IDE surface yet.
The workspace must help the user:
- see the full project tree,
- identify frontend-relevant source roots visually,
- open supported files into editor tabs,
- save editable supported documents through editor-local commands,
- consume frontend semantic-read UX provided through the integrated LSP phase when that phase is active,
- understand the active file context,
- and understand that broader IDE automation remains outside this wave.
The workspace must not pretend to offer:
- merge behavior,
- completion,
- rename, code actions, or formatting,
- or editor-owned semantic inference that bypasses the integrated LSP phase.
## Baseline Layout
The baseline workspace layout is:
- a left editor column,
- a central editor work area,
- a lower helper region,
- and a bottom status bar.
The left editor column must be a vertical stack containing:
- a functional `Project Navigator` at the top,
- and a reserved `Outline` region below it.
The central editor work area must contain:
- a top command bar containing at least `Save` and `Save All`,
- a tab strip at the top,
- and the editor body below it.
The lower helper region is present in this wave only as a passive placeholder.
## Component Ownership Rules
The `Code Editor` workspace must be implemented as a composition root plus child controls/panels rather than one monolithic render surface.
Rules:
- the workspace root should coordinate layout, workspace-level session state, and structural synchronization;
- the workspace root should consume project-session-owned document services such as `prometeu-vfs` rather than owning their lifecycle locally;
- the `Project Navigator`, tab strip, editor body, reserved `Outline`, helper region, and status bar should exist as explicit local surfaces;
- passive placeholder surfaces must remain visually real without implying unsupported functionality;
- and the first wave should preserve a clean path for later extraction of reusable Studio controls when the surfaces become stable enough to justify that move.
## Project Navigator Rules
### Primary Navigation Unit
- The primary navigation unit is the project file or directory.
- The navigator must cover the whole project rather than only frontend-tagged roots.
- Frontend tagging is supplementary context, not an inclusion filter.
### Structural Snapshot
- The navigator must consume a structural project-tree entity provided by `prometeu-vfs`.
- That structural tree must not replace the filesystem as the source of truth.
- Content snapshots are separate and are only required for files that are actually opened in the editor through `prometeu-vfs`.
- The navigator may request more targeted tree refresh operations than a full-tree reload when the `prometeu-vfs` contract provides them.
### Visibility and Ordering
- All project files and directories must be visible in the navigator.
- Hidden files must be included by default.
- Folders must sort before files.
- Items within those groups must sort alphabetically.
### Frontend-Aware Tagging
- `prometeu.json` plus the selected frontend may be used to tag relevant source roots or source directories.
- That tagging must not hide non-source project files.
- That tagging must not imply semantic/editor-language ownership by the navigator itself.
- Rendering those tags remains a Studio UI concern even when the source data comes from `prometeu-vfs`.
### Refresh Model
- The navigator must perform one initial refresh when the project opens.
- The navigator must expose a manual refresh action directly on the navigator surface and route that request through `prometeu-vfs`.
- Watcher-driven automatic refresh is explicitly deferred from this wave.
### Unsupported Files
- Unsupported or non-text files may still appear in the navigator.
- File support and unsupported-file classification belong to `prometeu-vfs`.
- Attempting to open such a file must show a simple modal stating that the file is not supported in this wave.
- The workspace must not fake a partial preview to imply unsupported file-format coverage.
## Tabs and File Opening Rules
- Selecting a supported file that is not already open must open it in a new tab.
- File opening must resolve document content through `prometeu-vfs`.
- The editor must maintain opened-file content in memory for the active Studio session only.
- Frontend-scoped supported documents may coexist in tabs with other editable supported documents.
- The tab strip must be responsive rather than fixed to one hardcoded tab count.
- Overflow tabs must remain accessible through an IntelliJ-style overflow control.
- The active tab must remain visible.
- The tab label must use only the file name with extension.
The first wave must not define:
- tab pinning,
- drag-and-drop tab reordering,
- grouped tabs,
- split editors.
Project-session-owned cross-session tab restoration is accepted for the project-local Studio state wave.
Rules:
- persisted editor restoration state must come from the project-session-owned `.studio/` state store rather than from an editor-owned ad hoc file;
- accepted restoration scope in this wave is limited to open tabs and active tab;
- editor layout restoration accepted in this wave may include divider-driven editor sizing, `Project Navigator` expansion state, and dock-panel configuration such as `Outline` and `Helper` open/closed state plus their restored size;
- restoration must degrade safely when a previously open file is missing or unsupported.
## Access-Mode and Save Model
The first `Code Editor` write wave is controlled rather than universally editable.
Rules:
- the workspace must treat `prometeu-vfs` as the canonical source of document access mode for supported files;
- the workspace must not infer frontend scope from path heuristics, local UI state, or ad hoc extension checks;
- supported frontend-scoped documents may be editable when `prometeu-vfs` classifies them as editable;
- editable scope is limited to the supported textual classes exposed by `prometeu-vfs` for this wave, including supported frontend sources;
- the workspace must expose save behavior only through an editor-local command bar containing at least `Save` and `Save All`;
- the global shell `Save` menu item must not be the save surface for this wave;
- save intent must route through `prometeu-vfs`, which remains the owner of persistence policy;
- the active indentation policy for editable documents MUST come from project-local setup rather than from file-content heuristics;
- the status-bar indentation chip MUST display the active configured policy, not inferred file contents;
- pressing `Tab` in an editable document MUST insert spaces according to the active configured indentation width;
- the active indentation policy MUST remain stable while the user edits document contents;
- opening a file whose existing indentation diverges from the configured policy MUST NOT trigger automatic reformatting;
- and the workspace must not define local merge/conflict behavior against disk changes.
## Outline Rules
- The `Outline` region must exist structurally in this wave.
- Before the integrated LSP semantic-read phase is implemented, it may show a discreet placeholder state.
- When the integrated LSP semantic-read phase is active, the `Outline` region may render frontend document-symbol structure and workspace-symbol-backed navigation owned by that phase.
- The workspace must not fake semantic outline structure before real semantic ownership exists.
## Helper Region Rules
- The helper region must exist structurally in this wave.
- Before the integrated LSP semantic-read phase is implemented, it may remain passive.
- It exists only to stabilize workspace composition and reserve future space.
- It may later host diagnostics or related semantic-read detail for frontend documents without changing workspace ownership.
- It must not become a substitute for the shell-level `Activity` surface.
## Integrated Semantic-Read Boundary
- Frontend documents may be editable when `prometeu-vfs` grants editable access mode.
- Diagnostics, document symbols, workspace symbols, outline-facing structure, definition, and frontend highlight must come through the integrated LSP semantic-read phase rather than from editor-local inference.
- Exact anchor positions for semantic scope indicators must come from frontend-owned structural metadata rather than Studio-local text scanning when that metadata is available.
- Opened frontend documents must be analyzed from the VFS-owned in-memory snapshot exposed through `prometeu-vfs`.
- Unopened frontend documents may be analyzed from filesystem-backed state exposed through the same `prometeu-vfs` boundary.
- Non-frontend highlight may remain Studio-local in this phase.
- Frontend highlight must come from the integrated LSP semantic-read phase and use frontend-owned semantic presentation contract data derived from `FrontendSpec`.
- Frontend semantic presentation resources must remain frontend-owned and must not be replaced by a Studio-owned generic fallback theme.
- When semantic presentation descriptor data or usable frontend resources are absent, the workspace must continue without semantic highlight for that frontend document.
- The workspace must not surface this condition as a product-facing editor error.
- The workspace must not treat semantic-read over editorial snapshots as authorization for build participation or for bypassing `prometeu-vfs` access policy.
## Inline Hint Rules
Inline hints must be treated as a host-generic editor capability rather than as a PBS-only feature.
The Studio `Code Editor` must render inline hints only from frontend-owned semantic payloads transported through the integrated semantic-read phase.
The editor must not invent:
- hint existence policy,
- hint text,
- hint categories,
- or host-owned semantic fallback hints.
If a frontend publishes valid inline hints through the accepted semantic-read contract, the editor must render them.
If a frontend does not publish inline hints for a construct, the editor must not synthesize one.
Inline hints are decorative only.
Therefore the editor must ensure that inline hints:
- do not modify document text;
- do not become part of persisted file content;
- do not participate as copied document text;
- do not redefine selection ranges;
- and do not redefine caret movement over the text model.
The completed capability must render inline hints as real inline editor decorations attached to document positions inside the editor flow.
An approximate visual implementation may exist only as an explicit transitional wave.
Any transitional approximation:
- must be documented as transitional;
- must not redefine the final editor contract;
- and must converge to real inline rendering in a later execution step.
When only some inline hint spans remain valid under degraded semantic analysis, the editor must continue rendering those valid spans rather than disabling all inline hints for the document.
## Scope Indicator Rules
The default semantic scope-indicator surface in the Code Editor must remain the gutter.
The first active-state wave must render at most two simultaneous semantic indicators:
- `activeContainer`
- `activeScope`
The editor must not restore the previous full ancestry stack as the default active-state presentation.
`activeScope` must be selected as the smallest structural range that contains the caret.
`activeContainer` must be selected as the immediate structural ancestor of `activeScope`.
If no valid structural ancestor exists, the editor may omit `activeContainer`.
The editor must not promote a higher semantic owner such as function, type, or module when a nearer structural ancestor exists.
The concrete visual presentation of `activeContainer` and `activeScope` remains frontend-owned. This specification intentionally does not fix:
- colors,
- stroke style,
- dashed versus solid treatment,
- opacity,
- thickness,
- or equivalent visual-emphasis details.
The Studio editor must only preserve the semantic distinction between the two indicator roles.
Exact guide start and end anchoring must consume the dedicated structural-anchor semantic surface defined by the integrated semantic-read specification.
The editor must not treat local brace scanning heuristics as the final production contract for exact anchor placement.
## Status Bar Rules
The status bar must remain mostly passive in this wave.
Its left side must show the breadcrumb path of the active file, such as `proj > src > file.pbs`.
Its right side must show:
- `L:C`,
- line separator,
- tabs/spaces mode,
- file extension or language,
- and access-mode state.
For this wave:
- `L:C` may remain a visual placeholder,
- editable non-frontend documents may show their current file-type state without implying build participation,
- hard `read-only` frontend documents must show an explicit `read-only` state,
- and the status-bar `read-only` surface should be shaped so it can evolve into a per-file toggle later without requiring a model rewrite.
## Session State Rules
Visual/editorial state in this wave is session-local only.
That includes at minimum:
- open tabs,
- active tab selection,
- tree expansion state,
- and similar visual editor state.
The following remain outside editor-owned session state:
- project-session ownership of `prometeu-vfs`,
- structural project-tree data returned by `prometeu-vfs`,
- support classification rules,
- document loading responsibilities,
- save persistence policy,
- and build-facing document ownership.
Editable documents may have session-local editorial snapshots while the project session remains open.
Those snapshots are editorial only and must not be treated as canonical build input by implication.
Persisting that state across Studio executions is deferred.
## Non-Goals
- merge/conflict resolution
- watcher-driven automatic refresh
- public `prometeu-vfs` event APIs
- helper panel functionality
- editor-owned semantic analysis that bypasses the integrated LSP phase
- frontend editing
- completion, rename, code actions, or formatting
- a normative editor event contract in this wave
- any implication that editor-owned in-memory snapshots participate in the build
- host-owned semantic inline hint policy
## Exit Criteria
This specification is complete enough when:
- the controlled write-wave scope is unambiguous,
- the `Project Navigator` and tab responsibilities are explicit,
- editor-local `Save` and `Save All` are part of the normative workspace contract,
- hard frontend `read-only` behavior is explicit in both warning and status-bar surfaces,
- the workspace boundary with the integrated frontend semantic-read phase is explicit,
- placeholder versus functional regions are clearly separated,
- and excluded frontend editing and completion behavior are clearly outside the current contract.

View File

@ -1,235 +0,0 @@
# Project Document VFS Specification
## Status
Active
## Applies To
- `prometeu-vfs`
- `prometeu-studio`
- the Studio project-document boundary for the first controlled Code Editor write wave
## Purpose
Define the normative Studio contract for `prometeu-vfs` as the project-document boundary consumed by Studio workspaces.
This specification stabilizes:
- `prometeu-vfs` module role,
- project-session ownership,
- the filesystem-backed first-wave contract,
- structural tree and document access responsibilities,
- canonical frontend scope and access policy ownership,
- editorial snapshot and save ownership for editable supported documents including frontend sources,
- the semantic-read consumer boundary used by the integrated LSP phase,
- the RPC-first public API baseline,
- and explicit first-wave exclusions such as public event publication and watchers.
## Authority and Precedence
This specification extends:
- [`1. Studio Shell and Workspace Layout Specification.md`](1.%20Studio%20Shell%20and%20Workspace%20Layout%20Specification.md)
- [`5. Code Editor Workspace Specification.md`](5.%20Code%20Editor%20Workspace%20Specification.md)
If this document conflicts with shell-wide Studio rules, the shell specification controls shell-wide behavior and this document controls the project-document boundary contract.
## Normative Inputs
`prometeu-vfs` must assume:
- the current migration wave carries only the Code Editor capabilities that already exist today,
- the initial backend is filesystem-backed,
- the boundary is project-scoped rather than product-global,
- the boundary must outlive workspace focus changes for an opened project,
- Studio UI remains the owner of visual tree state and error presentation,
- and the integrated `prometeu-lsp` phase consumes this boundary as a semantic-read client rather than as a persistence owner.
`prometeu-vfs` must not infer:
- watcher-driven refresh in this wave,
- public event API exposure in this wave,
- build participation for editorial snapshots by implication,
- or ownership over non-Studio domains such as packer assets.
## Module Role
`prometeu-vfs` is:
- the Studio project-document boundary,
- the owner of project structural tree data for the covered scope,
- the owner of project document resolution for the covered scope,
- the owner of file support and unsupported-file classification for the covered scope,
- the owner of document access policy for the covered scope,
- the owner of save persistence for editable documents in this wave,
- and the required path through which Studio accesses project-document filesystem state in this wave.
`prometeu-vfs` is not:
- a visual tree control,
- a Studio shell replacement,
- a product-wide universal filesystem layer,
- or a public event transport in this wave.
## Project Session Ownership
The primary `prometeu-vfs` instance must belong to the Studio project session.
Rules:
- a project-scoped `prometeu-vfs` instance must be created when a Studio project session is opened;
- that instance must remain alive while the project session remains open;
- switching workspace focus must not destroy or recreate the project-document state by default;
- the `Code Editor` workspace must consume the session-owned `prometeu-vfs` instance rather than owning the boundary lifecycle itself;
- the project-session scope must cover only the opened project.
## Filesystem Access Rules
For the scope covered by this specification, Studio must access the project filesystem through `prometeu-vfs`.
Rules:
- the first implementation must be filesystem-backed;
- direct filesystem reads for project tree and document loading must not remain a workspace-owned responsibility in the Code Editor;
- this rule does not grant `prometeu-vfs` ownership over unrelated domains or unrelated filesystem responsibilities elsewhere in the product.
## Structural Tree Contract
`prometeu-vfs` must expose a structural representation of the project tree.
Rules:
- the structural tree must cover project content only;
- the structural tree must contain structural and documentary data only;
- the structural tree must not contain visual presentation state;
- the structural tree must not contain navigator chrome decisions;
- the structural tree may include structural metadata useful to Studio consumers;
- consumers may request narrower refreshes than a full-tree reload when the request remains within the project boundary.
The following concerns remain outside `prometeu-vfs`:
- visual expansion state,
- selection state,
- focus state,
- reveal or scroll behavior,
- icon choice,
- and other view-model concerns of the navigator UI.
## Document Access Contract
`prometeu-vfs` must own document resolution for the covered Studio scope.
Rules:
- opening a supported file in the Code Editor must go through `prometeu-vfs`;
- the returned document content must describe only project-session document state for the opened project;
- `prometeu-vfs` must classify each supported document as at least editable or hard `read-only`;
- unsupported documents must remain distinguishable from supported `read-only` and supported editable documents;
- `prometeu-vfs` must own editable in-memory editorial snapshots for the current Studio session;
- save operations for editable documents in this wave must persist those editorial snapshots to disk through `prometeu-vfs`;
- Studio consumers must treat `prometeu-vfs` access policy as canonical rather than rederiving editability locally;
- and editorial snapshots must remain outside build-facing document state.
## Frontend Scope Contract
Frontend scope must be decided canonically inside `prometeu-vfs`.
Rules:
- `FrontendSpec.allowedExtensions` is the source of truth for frontend scope in this wave;
- the VFS document contract must expose a canonical frontend-compatible `typeId` or equivalent scope marker derived from that source of truth;
- consumers must not infer frontend scope from raw dynamic language identifiers, path heuristics, or local UI extension checks;
- frontend-scoped supported documents must be editable in this wave when they are supported by the current frontend;
- the initial editable set is limited to the currently supported textual classes represented as `text`, `json`, `ndjson`, `bash`, and supported frontend source documents resolved from `FrontendSpec.allowedExtensions`;
- and no additional editable class may be inferred by implementation convenience during this wave.
## Document Access Context
`prometeu-vfs` must reserve a single access-context entity for document-access-related values.
Rules:
- the access-context entity may remain session-local in this wave;
- it must already support lookup and mutation under current demand;
- future persistence may be added later without redesigning the access model;
- and the current reservation must not be treated as authorization for build participation or broader product-wide state ownership.
## Semantic-Read Consumer Boundary
`prometeu-vfs` must remain the source-of-truth document substrate consumed by the integrated LSP semantic-read phase.
Rules:
- opened frontend documents must be exposed to `prometeu-lsp` from the in-memory editorial snapshot held by `prometeu-vfs`, including dirty snapshots not yet saved to disk;
- unopened frontend documents may be exposed to `prometeu-lsp` from filesystem-backed state through the same boundary;
- `prometeu-lsp` must not bypass `prometeu-vfs` with ad hoc filesystem reads inside Studio UI code;
- `prometeu-lsp` must not become the owner of save, persistence, or access policy;
- semantic analysis over editorial snapshots must remain separate from build-facing document ownership;
- and this boundary must remain suitable for frontend diagnostics, symbols, definition, and highlight without implying frontend edit rights.
## Support and Plugin Rules
Support and unsupported-file decisions belong to `prometeu-vfs`.
Rules:
- file support classification must be decided inside `prometeu-vfs`;
- plugin or handler resolution for supported file access must be decided inside `prometeu-vfs`;
- Studio UI may render the resulting success or failure state, but it must not own the classification rule itself.
## Communication Model
The official first-wave `prometeu-vfs` API is RPC-oriented.
Rules:
- commands and queries are the public API baseline for this wave;
- internal runtime events may exist inside `prometeu-vfs`;
- those events must remain internal in this wave;
- internal events must not be promoted to public API by inference during planning or implementation.
## Refresh and Watchers
Refresh remains manual in this wave.
Rules:
- Studio consumers may request manual refresh through `prometeu-vfs`;
- watcher-driven automatic refresh is deferred;
- file watching must not be introduced implicitly while implementing this specification.
## Cross-Domain Boundary
`prometeu-vfs` belongs to the Studio domain.
Rules:
- `prometeu-vfs` serves `prometeu-lsp` as a consumer-facing substrate for the semantic-read phase;
- `prometeu-lsp` remains a separate layer and must not be renamed into `prometeu-vfs`;
- `prometeu-vfs` must not absorb packer responsibilities;
- `prometeu-vfs` must not redefine asset ownership;
- extending this boundary outside project-document concerns requires a new decision.
## Non-Goals
- public event publication
- watcher-driven refresh
- dirty tracking
- merge or conflict handling
- non-project content snapshots
- a generic product-wide filesystem abstraction
- treating editorial snapshots as canonical build input
## Exit Criteria
This specification is complete enough when:
- `prometeu-vfs` ownership is clearly separated from shell and workspace UI ownership,
- the project-session lifecycle rule is unambiguous,
- the structural tree contract is explicitly non-visual,
- frontend scope and access policy ownership are explicit,
- editable supported-document snapshot and save ownership are explicit,
- the semantic-read consumer boundary with `prometeu-lsp` is explicit,
- the RPC-first public API rule is explicit,
- and deferred public events and watchers are clearly out of scope.

View File

@ -1,183 +0,0 @@
# Integrated LSP Semantic Read Phase Specification
## Status
Active
## Applies To
- `prometeu-studio`
- `prometeu-vfs`
- `prometeu-lsp`
- the integrated frontend semantic phase in the Studio `Code Editor` workspace
## Purpose
Define the normative Studio contract for the integrated frontend semantic-read phase that arrives before any frontend editing release.
This specification stabilizes:
- the phase boundary between semantic ownership and frontend editing ownership,
- the ownership relationship between `prometeu-vfs`, `prometeu-lsp`, and the Studio editor,
- the minimum semantic capability set for frontend documents,
- the dedicated semantic surface used for structural anchors and guide-aware editor structure,
- the packaging expectations for the public `prometeu-lsp` API surface,
- and the explicit exclusions that remain outside this phase.
## Authority and Precedence
This specification extends:
- [`5. Code Editor Workspace Specification.md`](5.%20Code%20Editor%20Workspace%20Specification.md)
- [`6. Project Document VFS Specification.md`](6.%20Project%20Document%20VFS%20Specification.md)
If this document conflicts with shell-wide Studio rules, shell rules control shell behavior, the Code Editor specification controls workspace-local editor UX, the VFS specification controls document-boundary ownership, and this document controls the integrated semantic-read phase itself.
## Normative Inputs
The integrated semantic-read phase must assume:
- frontend-scoped documents may be editable under the controlled write wave exposed by `prometeu-vfs`,
- editable supported documents remain governed by the controlled write wave,
- `FrontendSpec.allowedExtensions` remains the source of truth for frontend scope,
- `FrontendSpec` is the canonical source of frontend semantic presentation contract data,
- `prometeu-vfs` owns document state, snapshots, persistence, and access policy,
- and semantic-read over editorial snapshots must remain separate from build-facing ownership.
## Phase Boundary
This phase is a semantic phase for frontend documents, not an ownership phase for frontend editing.
Rules:
- no capability in this phase may override `prometeu-vfs` access policy, save policy, or snapshot ownership;
- completion, rename, code actions, and formatting remain outside this phase.
## Ownership Rules
- `prometeu-vfs` owns document state, access policy, editorial snapshots, and persistence.
- `prometeu-lsp` is a semantic consumer above `prometeu-vfs`.
- the Studio `Code Editor` owns UX surfaces that render semantic-read output.
- each frontend owns its semantic vocabulary and semantic presentation resources.
- build-facing document ownership remains outside this phase.
`prometeu-lsp` must not become the owner of:
- save,
- persistence,
- document access policy,
- frontend semantic presentation assets,
- or frontend edit-right release.
## Document Source-of-Truth Rules
- opened frontend documents must be analyzed from the in-memory snapshot exposed by `prometeu-vfs`;
- unopened frontend documents may be analyzed from filesystem-backed state exposed through the same `prometeu-vfs` boundary;
- Studio UI code must not bypass `prometeu-vfs` with ad hoc file loading for semantic-read behavior;
- semantic-read over editorial snapshots must not be treated as canonical build input by implication.
## Minimum Capability Set
The minimum semantic capability set of this phase must include:
- diagnostics for frontend documents;
- document symbols for frontend documents;
- workspace symbols for frontend documents;
- outline-facing structural symbol data;
- go-to-definition for frontend documents;
- frontend semantic highlight.
The phase may ship the dedicated structural-anchor surface after the baseline capability set is stable, but Studio and frontend implementations must treat that surface as the canonical source of exact scope-indicator anchoring once it exists.
The phase may also ship a dedicated inline-hint semantic surface.
Once shipped, frontend and Studio implementations must treat that surface as the canonical source of decorative inline hint payloads.
## Highlight Ownership Rules
- frontend highlight must come from semantic information provided through `prometeu-lsp`;
- frontend highlight must use frontend-owned semantic presentation contract data derived from `FrontendSpec`;
- `prometeu-lsp` must expose a dedicated semantic presentation descriptor derived from the resolved `FrontendSpec`;
- that descriptor must remain simple and include frontend-owned `semanticKeys` plus frontend-owned `resources` in the same message surface;
- `prometeu-lsp` must not collapse frontend semantic keys into host-owned generic categories;
- Studio must project semantic keys mechanically to CSS classes without semantic translation;
- frontend semantic presentation resources must remain under frontend `resources/` and be resolved like ordinary Java resources;
- when semantic presentation descriptor data or usable resources are absent, Studio must continue without semantic highlight;
- this condition must not become a product-facing Studio error and may at most produce normal development logs;
- non-frontend highlight may remain on the existing Studio-local highlighting path in this phase.
## Inline Hint Ownership Rules
- frontend inline hints must come from semantic information provided through `prometeu-lsp`;
- frontend hint existence and hint payload semantics remain frontend-owned;
- `prometeu-lsp` must expose a dedicated transport contract for inline hints rather than relying on Studio-local inference;
- `prometeu-lsp` must preserve deterministic anchor information required for host rendering;
- `prometeu-lsp` must not invent host-owned hint policy when a frontend does not publish hints;
- Studio must render transported hints mechanically as host decorations rather than as document text;
- valid inline hint spans must survive partial degradation when the frontend can still produce them;
- invalid or missing hint spans may be omitted locally;
- Studio must not disable all inline hints for a document solely because some hint spans are unavailable.
## Structural Anchor Semantic Surface
Exact structural anchoring for editor scope indicators must not be overloaded into `documentSymbols`.
`documentSymbols` remains the outline and navigation surface.
Guide-oriented structure must be exposed through a dedicated semantic surface for structural anchors and related guide metadata.
That dedicated surface must be frontend-owned and language-agnostic.
The contract must describe generic structural positions rather than PBS-specific brace semantics.
The surface must be able to represent structures whose boundaries are expressed by:
- braces;
- keywords;
- indentation-derived blocks;
- or mixed delimiter schemes.
At minimum, the dedicated structural-anchor surface must be able to communicate:
- structural ranges that can participate in active-scope selection;
- immediate parentage or equivalent information needed to recover the nearest ancestor relationship;
- exact structural anchor positions used for guide start and end placement;
- enough structural identity to distinguish the active structural range from its immediate container.
The semantic-read contract must not require Studio to scan source text in order to discover the final structural anchor positions.
Temporary local heuristics may exist only as development aids. They must not become the canonical production contract.
The exact serialized schema of this dedicated surface may evolve in implementation plans, but any accepted schema must preserve the invariants above.
## Packaging Rules
The public `prometeu-lsp` API should follow flat packaging similar to `prometeu-packer-api`.
Rules:
- public contract surfaces should remain shallow and explicit;
- packages such as `dtos`, `messages`, and `events` are the preferred shape for public API grouping where those surfaces exist;
- deep public packaging by internal implementation concern should be avoided;
- runtime or implementation layering may exist internally, but it must not leak into the public contract surface by default.
## Non-Goals
- frontend save policy
- completion
- rename
- code actions
- formatting
- build participation inferred from editorial snapshots
- external-editor compatibility requirements
- Studio-owned semantic hint generation
## Exit Criteria
This specification is complete enough when:
- the semantic-read versus frontend-editing phase boundary is explicit,
- `prometeu-vfs` and `prometeu-lsp` ownership remains unambiguous,
- the minimum semantic capability set is explicit,
- frontend and non-frontend highlight ownership is clearly separated,
- and the excluded editing and completion capabilities remain unambiguous.

View File

@ -22,7 +22,6 @@ This specification implements the accepted decision for project-local Studio sta
It extends:
- [`1. Studio Shell and Workspace Layout Specification.md`](1.%20Studio%20Shell%20and%20Workspace%20Layout%20Specification.md)
- [`5. Code Editor Workspace Specification.md`](5.%20Code%20Editor%20Workspace%20Specification.md)
## Core Rules
@ -31,7 +30,7 @@ It extends:
3. The main session-restoration store MUST remain a single file at `.studio/state.json`.
4. The main session-restoration store MUST be versioned.
5. The main session-restoration store MUST load when the project session opens.
6. Restorable shell and editor state accepted by this wave MUST restore when the project shell mounts.
6. Restorable shell state accepted by this wave MUST restore when the project shell mounts.
7. The main session-restoration store MUST save when the project shell or project session closes.
8. Missing, invalid, or schema-incompatible session-restoration state MUST fall back to safe default values and MUST NOT block project open.
9. Project-local setup configuration MUST live in a separate file at `.studio/setup.json`.
@ -44,13 +43,11 @@ The main session-restoration store MUST include:
- `shellLayout`
- `openShellState`
- `editorRestoration`
Wave-1 accepted meaning is:
- `shellLayout`: restorable layout state already accepted by implementation, including divider positions and equivalent layout proportions for accepted workspaces such as `Assets` and `Code Editor`, `Project Navigator` expansion state, and editor dock-panel open/closed plus restored-size configuration;
- `shellLayout`: restorable layout state already accepted by implementation, including divider positions and equivalent layout proportions for accepted workspaces such as `Assets`;
- `openShellState`: which workspace or equivalent shell-owned project view was open;
- `editorRestoration`: open editor tabs and active tab.
## Project-Local Setup Configuration
@ -61,8 +58,8 @@ Rules:
- project-local setup MUST live in `.studio/setup.json`;
- project-local setup MUST be treated as project configuration rather than session state;
- project-local setup MAY contain runtime-oriented values such as the Prometeu runtime path;
- project-local setup MUST own the project-wide editor indentation policy;
- the editor indentation policy MUST define at least indentation mode and indentation width;
- project-local setup MAY continue carrying project-wide indentation policy defaults for future code-tooling integrations;
- the indentation policy MUST define at least indentation mode and indentation width;
- when setup does not provide an explicit indentation policy, consumers MUST resolve it to `spaces` with width `4`;
- project-local setup MAY grow with additional manual or automatic configuration keys over time;
- consumers such as `Play` MUST read project-local setup from the dedicated setup file rather than from `.studio/state.json`;

View File

@ -54,10 +54,7 @@ The current Studio core corpus is:
2. [`2. Studio UI Foundations Specification.md`](2.%20Studio%20UI%20Foundations%20Specification.md)
3. [`3. Studio Components Module Specification.md`](3.%20Studio%20Components%20Module%20Specification.md)
4. [`4. Assets Workspace Specification.md`](4.%20Assets%20Workspace%20Specification.md)
5. [`5. Code Editor Workspace Specification.md`](5.%20Code%20Editor%20Workspace%20Specification.md)
6. [`6. Project Document VFS Specification.md`](6.%20Project%20Document%20VFS%20Specification.md)
7. [`7. Integrated LSP Semantic Read Phase Specification.md`](7.%20Integrated%20LSP%20Semantic%20Read%20Phase%20Specification.md)
8. [`8. Project-Local Studio State Specification.md`](8.%20Project-Local%20Studio%20State%20Specification.md)
5. [`8. Project-Local Studio State Specification.md`](8.%20Project-Local%20Studio%20State%20Specification.md)
## Reading Order
@ -67,19 +64,14 @@ Recommended order:
2. shared UI foundations;
3. components module policy;
4. assets workspace behavior;
5. project document VFS boundary;
6. code editor workspace behavior;
7. integrated LSP semantic-read behavior;
8. project-local Studio state behavior.
5. project-local Studio state behavior.
## Current Wave Notes
The current `Code Editor` wave is not globally read-only anymore.
The embedded Studio code-editor stack was removed by `DEC-0031`.
Normative reminders:
- editable scope is limited to supported non-frontend textual documents classified by `prometeu-vfs`;
- frontend-scoped supported documents remain hard `read-only`;
- the editor-local `Save` and `Save All` surfaces belong to the `Code Editor` workspace rather than the shell;
- editorial in-memory snapshots remain outside build-facing state;
- frontend semantic-read capability lands before any frontend editing release and is documented by the integrated LSP phase specification.
- the active Studio workspace corpus is `Assets`, `Debug`, and `Shipper`;
- project-local Studio session state remains normative and is owned by `StudioProjectSession`;
- any future external-editor backend or replacement language-service architecture requires a new accepted decision before new Studio specs are introduced.

View File

@ -6,6 +6,7 @@ jacoco = "0.8.12"
lombok = "1.18.32"
junit-jupiter = "5.12.1"
slf4j = "2.0.7"
lsp4j = "1.0.0"
[libraries]
javafx-controls = { group = "org.openjfx", name = "javafx-controls", version.ref = "javafx" }
@ -20,6 +21,7 @@ lombok = { group = "org.projectlombok", name = "lombok", version.ref = "lombok"
junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version.ref = "junit-jupiter" }
slf4j-api = { group = "org.slf4j", name = "slf4j-api", version.ref = "slf4j" }
slf4j-simple = { group = "org.slf4j", name = "slf4j-simple", version.ref = "slf4j" }
lsp4j = { group = "org.eclipse.lsp4j", name = "org.eclipse.lsp4j", version.ref = "lsp4j" }
[plugins]
javafx = { id = "org.openjfx.javafxplugin", version = "0.1.0" }

View File

@ -7,8 +7,6 @@ dependencies {
implementation(project(":prometeu-infra"))
implementation(project(":prometeu-lsp:prometeu-lsp-api"))
implementation(project(":prometeu-lsp:prometeu-lsp-v1"))
implementation(project(":prometeu-vfs:prometeu-vfs-api"))
implementation(project(":prometeu-vfs:prometeu-vfs-v1"))
implementation(project(":prometeu-packer:prometeu-packer-api"))
implementation(project(":prometeu-packer:prometeu-packer-v1"))
implementation(project(":prometeu-studio"))
@ -23,4 +21,4 @@ application {
javafx {
version = libs.versions.javafx.get()
modules("javafx.controls", "javafx.fxml")
}
}

View File

@ -2,14 +2,12 @@ package p.studio;
import com.fasterxml.jackson.databind.ObjectMapper;
import p.packer.Packer;
import p.studio.lsp.LspServiceFactory;
import p.studio.lsp.LspServiceFactoryImpl;
import p.studio.lsp.api.LspServerLifecycle;
import p.studio.lsp.events.StudioEventBus;
import p.studio.lsp.events.StudioPackerEventAdapter;
import p.studio.lsp.services.LspServerLifecycleImpl;
import p.studio.utilities.ThemeService;
import p.studio.utilities.i18n.I18nService;
import p.studio.vfs.FilesystemProjectDocumentVfsFactory;
import p.studio.vfs.ProjectDocumentVfsFactory;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@ -21,22 +19,20 @@ public final class AppContainer implements Container {
private final ThemeService themeService;
private final StudioEventBus studioEventBus;
private final ObjectMapper mapper;
private final LspServiceFactory lspServiceFactory;
private final ProjectDocumentVfsFactory projectDocumentVfsFactory;
private final EmbeddedPacker embeddedPacker;
private final StudioBackgroundTasks backgroundTasks;
private final LspServerLifecycle lspServerLifecycle;
public AppContainer() {
this.i18nService = new I18nService();
this.themeService = new ThemeService();
this.studioEventBus = new StudioEventBus();
this.mapper = new ObjectMapper();
this.lspServiceFactory = new LspServiceFactoryImpl();
this.projectDocumentVfsFactory = new FilesystemProjectDocumentVfsFactory();
final ExecutorService backgroundExecutor = Executors.newFixedThreadPool(2, new StudioWorkerThreadFactory());
this.backgroundTasks = new StudioBackgroundTasks(backgroundExecutor);
final Packer packer = Packer.bootstrap(this.mapper, new StudioPackerEventAdapter(studioEventBus));
this.embeddedPacker = new EmbeddedPacker(packer.workspaceService(), packer::close);
this.lspServerLifecycle = new LspServerLifecycleImpl();
}
@Override
@ -59,16 +55,6 @@ public final class AppContainer implements Container {
return mapper;
}
@Override
public LspServiceFactory getPrometeuLspServiceFactory() {
return lspServiceFactory;
}
@Override
public ProjectDocumentVfsFactory getProjectDocumentVfsFactory() {
return projectDocumentVfsFactory;
}
@Override
public EmbeddedPacker getPacker() {
return embeddedPacker;
@ -79,6 +65,11 @@ public final class AppContainer implements Container {
return backgroundTasks;
}
@Override
public p.studio.lsp.api.LspServerLifecycle getLspServerLifecycle() {
return lspServerLifecycle;
}
@Override
public void shutdownContainer() {
embeddedPacker.shutdown();

View File

@ -1,8 +1,7 @@
package p.studio.compiler;
import p.studio.compiler.models.FrontendSpec;
import p.studio.compiler.models.FrontendSemanticPresentationSpec;
import p.studio.compiler.pbs.PbsSemanticKind;
import p.studio.compiler.models.FrontendSpec;
import p.studio.utilities.structures.ReadOnlySet;
import java.util.List;
@ -15,7 +14,9 @@ public class PBSDefinitions {
.sourceRoots(ReadOnlySet.from("src"))
.stdlibVersions(List.of(FrontendSpec.Stdlib.asDefault(1)))
.semanticPresentation(new FrontendSemanticPresentationSpec(
PbsSemanticKind.semanticKeys(),
List.of("/themes/pbs/semantic-highlighting.css")))
List.of(PBSFrontendThemes.DEFAULT_PBS),
"pbs-default",
List.of(PBSFrontendHostProjections.VSCODE)))
.semanticTokenProvider(new PBSSemanticTokenProvider())
.build();
}

View File

@ -0,0 +1,78 @@
package p.studio.compiler;
import p.studio.compiler.models.FrontendHostProjectionEntrySpec;
import p.studio.compiler.models.FrontendHostProjectionSpec;
import p.studio.compiler.pbs.PbsSemanticKind;
import java.util.Arrays;
import java.util.List;
import static p.studio.compiler.pbs.PbsSemanticKind.*;
public final class PBSFrontendHostProjections {
public static final String VSCODE_HOST_ID = "vscode";
public static final FrontendHostProjectionSpec VSCODE = new FrontendHostProjectionSpec(
VSCODE_HOST_ID,
Arrays.stream(PbsSemanticKind.values())
.map(PBSFrontendHostProjections::mapVscodeProjection)
.toList());
private PBSFrontendHostProjections() {
}
private static FrontendHostProjectionEntrySpec mapVscodeProjection(final PbsSemanticKind kind) {
return switch (kind) {
case COMMENT -> projection(COMMENT.semanticKey(), "comment");
case STRING -> projection(STRING.semanticKey(), "string");
case NUMBER -> projection(NUMBER.semanticKey(), "number");
case LITERAL -> projection(LITERAL.semanticKey(), "enumMember", "variable");
case LIFECYCLE -> projection(LIFECYCLE.semanticKey(), "function", "variable", "async");
case KEYWORD -> projection(KEYWORD.semanticKey(), "keyword");
case OPERATOR -> projection(OPERATOR.semanticKey(), "operator", "keyword");
case PUNCTUATION -> projection(PUNCTUATION.semanticKey(), "operator", "keyword");
case FUNCTION -> projection(FUNCTION.semanticKey(), "function");
case METHOD -> projection(METHOD.semanticKey(), "method", "function");
case CONSTRUCTOR -> projection(CONSTRUCTOR.semanticKey(), "class", "type", "declaration");
case STRUCT -> projection(STRUCT.semanticKey(), "struct", "type");
case CONTRACT -> projection(CONTRACT.semanticKey(), "interface", "type");
case HOST -> projection(HOST.semanticKey(), "class", "type", "defaultLibrary");
case BUILTIN_TYPE -> projection(BUILTIN_TYPE.semanticKey(), "type", "variable", "defaultLibrary");
case SERVICE -> projection(SERVICE.semanticKey(), "class", "type", "defaultLibrary");
case ASSET -> projection(ASSET.semanticKey(), "variable", "string", "readonly");
case ERROR -> projection(ERROR.semanticKey(), "enum", "type");
case ENUM -> projection(ENUM.semanticKey(), "enum", "type");
case CALLBACK -> projection(CALLBACK.semanticKey(), "interface", "type");
case GLOBAL -> projection(GLOBAL.semanticKey(), "variable", "variable", "readonly");
case CONST -> projection(CONST.semanticKey(), "variable", "variable", "readonly");
case IMPLEMENTS -> projection(IMPLEMENTS.semanticKey(), "keyword");
case IDENTIFIER -> projection(IDENTIFIER.semanticKey(), "variable");
};
}
private static FrontendHostProjectionEntrySpec projection(
final String semanticKey,
final String hostTokenType) {
return new FrontendHostProjectionEntrySpec(semanticKey, hostTokenType, List.of(), hostTokenType, List.of());
}
private static FrontendHostProjectionEntrySpec projection(
final String semanticKey,
final String hostTokenType,
final String fallbackTokenType) {
return new FrontendHostProjectionEntrySpec(semanticKey, hostTokenType, List.of(), fallbackTokenType, List.of());
}
private static FrontendHostProjectionEntrySpec projection(
final String semanticKey,
final String hostTokenType,
final String fallbackTokenType,
final String modifier) {
return new FrontendHostProjectionEntrySpec(
semanticKey,
hostTokenType,
List.of(modifier),
fallbackTokenType,
List.of(modifier));
}
}

View File

@ -0,0 +1,46 @@
package p.studio.compiler;
import p.studio.compiler.models.FrontendEditorPaletteSpec;
import p.studio.compiler.models.FrontendTokenStyleSpec;
import p.studio.compiler.models.FrontendVisualThemeSpec;
import java.util.List;
public class PBSFrontendThemes {
public static final FrontendVisualThemeSpec DEFAULT_PBS = new FrontendVisualThemeSpec(
"pbs-default",
"PBS Default",
new FrontendEditorPaletteSpec(
"#d9e2ec",
"#2f3f5b",
"#23314a",
"#6b7f99",
"#241f38",
"#b392f0",
"#f5efff"),
List.of(
new FrontendTokenStyleSpec("pbs-comment", "#8b949e", true, false, false),
new FrontendTokenStyleSpec("pbs-string", "#d8b86a", false, false, false),
new FrontendTokenStyleSpec("pbs-number", "#ff8cc6", false, false, false),
new FrontendTokenStyleSpec("pbs-literal", "#7fb8ff", false, false, false),
new FrontendTokenStyleSpec("pbs-lifecycle", "#ff7b72", false, true, false),
new FrontendTokenStyleSpec("pbs-keyword", "#7fb8ff", false, false, false),
new FrontendTokenStyleSpec("pbs-operator", "#c9d1d9", false, false, false),
new FrontendTokenStyleSpec("pbs-punctuation", "#8b949e", false, false, false),
new FrontendTokenStyleSpec("pbs-function", "#7ee787", false, true, false),
new FrontendTokenStyleSpec("pbs-method", "#7ee787", false, false, false),
new FrontendTokenStyleSpec("pbs-constructor", "#7ee787", false, true, false),
new FrontendTokenStyleSpec("pbs-struct", "#56d4dd", true, false, false),
new FrontendTokenStyleSpec("pbs-contract", "#79c0ff", false, false, true),
new FrontendTokenStyleSpec("pbs-host", "#c297ff", false, false, false),
new FrontendTokenStyleSpec("pbs-builtin-type", "#56d4dd", false, false, false),
new FrontendTokenStyleSpec("pbs-service", "#ff8e7a", false, true, false),
new FrontendTokenStyleSpec("pbs-asset", "#c297ff", false, false, false),
new FrontendTokenStyleSpec("pbs-error", "#ff7b72", false, false, true),
new FrontendTokenStyleSpec("pbs-enum", "#56d4dd", false, true, false),
new FrontendTokenStyleSpec("pbs-callback", "#ffb3e6", false, false, false),
new FrontendTokenStyleSpec("pbs-global", "#f778ba", true, false, false),
new FrontendTokenStyleSpec("pbs-const", "#ffab70", false, true, false),
new FrontendTokenStyleSpec("pbs-implements", "#a5d6ff", false, false, false),
new FrontendTokenStyleSpec("pbs-identifier", "#d4d4d4", false, false, false)));
}

View File

@ -0,0 +1,365 @@
package p.studio.compiler;
import p.studio.compiler.models.FrontendSemanticToken;
import p.studio.compiler.models.FrontendSemanticTokenProvider;
import p.studio.compiler.pbs.ast.PbsAst;
import p.studio.compiler.pbs.PbsSemanticKind;
import p.studio.compiler.pbs.lexer.PbsLexer;
import p.studio.compiler.pbs.lexer.PbsToken;
import p.studio.compiler.pbs.lexer.PbsTokenKind;
import p.studio.compiler.pbs.parser.PbsBarrelParser;
import p.studio.compiler.pbs.parser.PbsParser;
import p.studio.compiler.pbs.stdlib.ResourceStdlibEnvironmentResolver;
import p.studio.compiler.pbs.stdlib.StdlibModuleSource;
import p.studio.compiler.source.diagnostics.DiagnosticSink;
import p.studio.compiler.source.identifiers.FileId;
import p.studio.utilities.structures.ReadOnlyList;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public final class PBSSemanticTokenProvider implements FrontendSemanticTokenProvider {
private static final Set<String> BUILTIN_TYPE_NAMES = Set.of("int", "float", "bool", "string", "i32", "i64", "f32", "f64");
private static final int DEFAULT_STDLIB_VERSION = 1;
private static final ResourceStdlibEnvironmentResolver STDLIB_RESOLVER = new ResourceStdlibEnvironmentResolver();
private static final Map<PbsTokenKind, PbsSemanticKind> DECLARATION_KINDS = declarationKinds();
@Override
public List<FrontendSemanticToken> tokenize(final String documentText) {
final String effectiveText = documentText == null ? "" : documentText;
final ReadOnlyList<PbsToken> tokens = PbsLexer.lex(effectiveText, FileId.none(), DiagnosticSink.empty());
final Map<String, PbsSemanticKind> declaredKindsByName = collectDeclaredKinds(tokens);
final Set<Integer> assetReferenceTokenIndexes = collectAssetReferenceTokenIndexes(tokens);
final ArrayList<FrontendSemanticToken> semanticTokens = new ArrayList<>();
for (int index = 0; index < tokens.size(); index += 1) {
final PbsToken token = tokens.get(index);
final PbsSemanticKind semanticKind = classifyToken(tokens, index, declaredKindsByName, assetReferenceTokenIndexes);
if (semanticKind == null) {
continue;
}
semanticTokens.add(new FrontendSemanticToken(token.start(), token.end(), semanticKind.semanticKey()));
}
return List.copyOf(semanticTokens);
}
private PbsSemanticKind classifyToken(
final ReadOnlyList<PbsToken> tokens,
final int index,
final Map<String, PbsSemanticKind> declaredKindsByName,
final Set<Integer> assetReferenceTokenIndexes) {
final PbsToken token = tokens.get(index);
final PbsSemanticKind lexicalKind = PbsSemanticKind.forToken(token);
if (lexicalKind != null) {
return lexicalKind;
}
if (token.kind() == PbsTokenKind.IDENTIFIER) {
if (assetReferenceTokenIndexes.contains(index)) {
return PbsSemanticKind.ASSET;
}
if (isLifecycleToken(tokens, index, token)) {
return PbsSemanticKind.LIFECYCLE;
}
return classifyIdentifierToken(tokens, index, token, declaredKindsByName);
}
return null;
}
private PbsSemanticKind classifyIdentifierToken(
final ReadOnlyList<PbsToken> tokens,
final int index,
final PbsToken token,
final Map<String, PbsSemanticKind> declaredKindsByName) {
final PbsTokenKind previousKind = previousSignificantKind(tokens, index);
final PbsTokenKind nextKind = nextSignificantKind(tokens, index);
final boolean typeReferenceContext = isTypeReferenceContext(previousKind);
if (previousKind == PbsTokenKind.STRUCT) {
return PbsSemanticKind.STRUCT;
}
if (previousKind == PbsTokenKind.CONTRACT) {
return PbsSemanticKind.CONTRACT;
}
if (previousKind == PbsTokenKind.HOST) {
return PbsSemanticKind.HOST;
}
if (previousKind == PbsTokenKind.SERVICE) {
return PbsSemanticKind.SERVICE;
}
if (previousKind == PbsTokenKind.ENUM || previousKind == PbsTokenKind.ERROR) {
return PbsSemanticKind.ENUM;
}
if (previousKind == PbsTokenKind.CALLBACK) {
return PbsSemanticKind.CALLBACK;
}
if (previousKind == PbsTokenKind.GLOBAL) {
return PbsSemanticKind.GLOBAL;
}
if (previousKind == PbsTokenKind.CONST) {
return PbsSemanticKind.CONST;
}
if (previousKind == PbsTokenKind.CTOR || previousKind == PbsTokenKind.NEW) {
return PbsSemanticKind.CONSTRUCTOR;
}
if (previousKind == PbsTokenKind.BUILTIN) {
return PbsSemanticKind.BUILTIN_TYPE;
}
final PbsSemanticKind declaredKind = declaredKindsByName.get(token.lexeme());
if (declaredKind != null) {
if (previousKind == PbsTokenKind.DOT && nextKind == PbsTokenKind.LEFT_PAREN) {
return PbsSemanticKind.METHOD;
}
if (nextKind == PbsTokenKind.DOT
|| previousKind == PbsTokenKind.NEW
|| typeReferenceContext) {
return declaredKind;
}
}
if (previousKind == PbsTokenKind.DOT && nextKind == PbsTokenKind.LEFT_PAREN) {
return PbsSemanticKind.METHOD;
}
if (nextKind == PbsTokenKind.LEFT_PAREN) {
return PbsSemanticKind.FUNCTION;
}
if (typeReferenceContext && BUILTIN_TYPE_NAMES.contains(token.lexeme())) {
return PbsSemanticKind.BUILTIN_TYPE;
}
return PbsSemanticKind.IDENTIFIER;
}
private Map<String, PbsSemanticKind> collectDeclaredKinds(final ReadOnlyList<PbsToken> tokens) {
final Map<String, PbsSemanticKind> declaredKindsByName = new HashMap<>();
for (int index = 0; index < tokens.size() - 1; index += 1) {
final PbsToken token = tokens.get(index);
final PbsToken nextToken = tokens.get(index + 1);
if (nextToken.kind() != PbsTokenKind.IDENTIFIER) {
continue;
}
final PbsSemanticKind declarationKind = declarationKind(token.kind());
if (declarationKind == null) {
continue;
}
declaredKindsByName.put(nextToken.lexeme(), declarationKind);
}
declaredKindsByName.putAll(collectStdlibImportedKinds(tokens));
return Map.copyOf(declaredKindsByName);
}
private Set<Integer> collectAssetReferenceTokenIndexes(final ReadOnlyList<PbsToken> tokens) {
final Set<Integer> indexes = new HashSet<>();
for (int index = 0; index < tokens.size(); index += 1) {
final PbsToken token = tokens.get(index);
if (token.kind() != PbsTokenKind.IDENTIFIER || !"assets".equals(token.lexeme())) {
continue;
}
final Integer firstDotIndex = nextSignificantIndex(tokens, index);
if (firstDotIndex == null || tokens.get(firstDotIndex).kind() != PbsTokenKind.DOT) {
continue;
}
indexes.add(index);
Integer cursor = firstDotIndex;
while (cursor != null && tokens.get(cursor).kind() == PbsTokenKind.DOT) {
final Integer memberIndex = nextSignificantIndex(tokens, cursor);
if (memberIndex == null || tokens.get(memberIndex).kind() != PbsTokenKind.IDENTIFIER) {
break;
}
indexes.add(memberIndex);
cursor = nextSignificantIndex(tokens, memberIndex);
}
}
return Set.copyOf(indexes);
}
private PbsSemanticKind declarationKind(final PbsTokenKind kind) {
return DECLARATION_KINDS.get(kind);
}
private Map<String, PbsSemanticKind> collectStdlibImportedKinds(final ReadOnlyList<PbsToken> tokens) {
final DiagnosticSink diagnostics = DiagnosticSink.empty();
final PbsAst.File file = PbsParser.parse(tokens, FileId.none(), diagnostics);
final Map<String, PbsSemanticKind> importedKindsByLocalName = new HashMap<>();
for (final PbsAst.ImportDecl importDecl : file.imports()) {
if (importDecl.items().isEmpty()) {
continue;
}
final Map<String, PbsSemanticKind> exportedKinds = resolveStdlibExportKinds(importDecl.moduleRef());
if (exportedKinds.isEmpty()) {
continue;
}
for (final Map.Entry<String, PbsSemanticKind> entry : exportedKinds.entrySet()) {
if (entry.getValue() == PbsSemanticKind.BUILTIN_TYPE) {
importedKindsByLocalName.put(entry.getKey(), entry.getValue());
}
}
for (final PbsAst.ImportItem item : importDecl.items()) {
final PbsSemanticKind semanticKind = exportedKinds.get(item.name());
if (semanticKind == null) {
continue;
}
importedKindsByLocalName.put(item.alias() == null ? item.name() : item.alias(), semanticKind);
}
}
return importedKindsByLocalName;
}
private Map<String, PbsSemanticKind> resolveStdlibExportKinds(final PbsAst.ModuleRef moduleRef) {
final var stdlibEnvironment = STDLIB_RESOLVER.resolve(DEFAULT_STDLIB_VERSION);
return stdlibEnvironment.resolveModule(moduleRef.project(), moduleRef.pathSegments())
.map(this::parseStdlibVisibleKinds)
.orElseGet(Map::of);
}
private Map<String, PbsSemanticKind> parseStdlibVisibleKinds(final StdlibModuleSource moduleSource) {
final Map<String, PbsSemanticKind> visibleKinds = new HashMap<>(parseBarrelExportKinds(moduleSource.barrelSource()));
for (final StdlibModuleSource.SourceFile sourceFile : moduleSource.sourceFiles()) {
visibleKinds.putAll(parseBuiltinTypeKinds(sourceFile.source()));
}
return visibleKinds;
}
private Map<String, PbsSemanticKind> parseBarrelExportKinds(final String barrelSource) {
final ReadOnlyList<PbsToken> barrelTokens = PbsLexer.lex(barrelSource, FileId.none(), DiagnosticSink.empty());
final PbsAst.BarrelFile barrelFile = PbsBarrelParser.parse(barrelTokens, FileId.none(), DiagnosticSink.empty());
final Map<String, PbsSemanticKind> exportedKinds = new HashMap<>();
for (final PbsAst.BarrelItem item : barrelFile.items()) {
final PbsSemanticKind semanticKind = semanticKindForBarrelItem(item);
if (semanticKind == null) {
continue;
}
if (item instanceof PbsAst.BarrelStructItem structItem && structItem.visibility() == PbsAst.Visibility.PUB) {
exportedKinds.put(structItem.name(), semanticKind);
} else if (item instanceof PbsAst.BarrelContractItem contractItem && contractItem.visibility() == PbsAst.Visibility.PUB) {
exportedKinds.put(contractItem.name(), semanticKind);
} else if (item instanceof PbsAst.BarrelHostItem hostItem && hostItem.visibility() == PbsAst.Visibility.PUB) {
exportedKinds.put(hostItem.name(), semanticKind);
} else if (item instanceof PbsAst.BarrelErrorItem errorItem && errorItem.visibility() == PbsAst.Visibility.PUB) {
exportedKinds.put(errorItem.name(), semanticKind);
} else if (item instanceof PbsAst.BarrelEnumItem enumItem && enumItem.visibility() == PbsAst.Visibility.PUB) {
exportedKinds.put(enumItem.name(), semanticKind);
} else if (item instanceof PbsAst.BarrelServiceItem serviceItem && serviceItem.visibility() == PbsAst.Visibility.PUB) {
exportedKinds.put(serviceItem.name(), semanticKind);
} else if (item instanceof PbsAst.BarrelGlobalItem globalItem && globalItem.visibility() == PbsAst.Visibility.PUB) {
exportedKinds.put(globalItem.name(), semanticKind);
} else if (item instanceof PbsAst.BarrelConstItem constItem && constItem.visibility() == PbsAst.Visibility.PUB) {
exportedKinds.put(constItem.name(), semanticKind);
} else if (item instanceof PbsAst.BarrelCallbackItem callbackItem && callbackItem.visibility() == PbsAst.Visibility.PUB) {
exportedKinds.put(callbackItem.name(), semanticKind);
}
}
return exportedKinds;
}
private Map<String, PbsSemanticKind> parseBuiltinTypeKinds(final String source) {
final ReadOnlyList<PbsToken> sourceTokens = PbsLexer.lex(source, FileId.none(), DiagnosticSink.empty());
final PbsAst.File file = PbsParser.parse(
sourceTokens,
FileId.none(),
DiagnosticSink.empty(),
PbsParser.ParseMode.INTERFACE_MODULE);
final Map<String, PbsSemanticKind> builtinKinds = new HashMap<>();
for (final PbsAst.TopDecl topDecl : file.topDecls()) {
if (topDecl instanceof PbsAst.BuiltinTypeDecl builtinTypeDecl) {
builtinKinds.put(builtinTypeDecl.name(), PbsSemanticKind.BUILTIN_TYPE);
}
}
return builtinKinds;
}
private PbsSemanticKind semanticKindForBarrelItem(final PbsAst.BarrelItem item) {
return switch (item) {
case PbsAst.BarrelStructItem ignored -> PbsSemanticKind.STRUCT;
case PbsAst.BarrelContractItem ignored -> PbsSemanticKind.CONTRACT;
case PbsAst.BarrelHostItem ignored -> PbsSemanticKind.HOST;
case PbsAst.BarrelErrorItem ignored -> PbsSemanticKind.ENUM;
case PbsAst.BarrelEnumItem ignored -> PbsSemanticKind.ENUM;
case PbsAst.BarrelServiceItem ignored -> PbsSemanticKind.SERVICE;
case PbsAst.BarrelGlobalItem ignored -> PbsSemanticKind.GLOBAL;
case PbsAst.BarrelConstItem ignored -> PbsSemanticKind.CONST;
case PbsAst.BarrelCallbackItem ignored -> PbsSemanticKind.CALLBACK;
case PbsAst.BarrelFunctionItem ignored -> null;
};
}
private static Map<PbsTokenKind, PbsSemanticKind> declarationKinds() {
final Map<PbsTokenKind, PbsSemanticKind> kinds = new EnumMap<>(PbsTokenKind.class);
kinds.put(PbsTokenKind.STRUCT, PbsSemanticKind.STRUCT);
kinds.put(PbsTokenKind.CONTRACT, PbsSemanticKind.CONTRACT);
kinds.put(PbsTokenKind.HOST, PbsSemanticKind.HOST);
kinds.put(PbsTokenKind.SERVICE, PbsSemanticKind.SERVICE);
kinds.put(PbsTokenKind.ENUM, PbsSemanticKind.ENUM);
kinds.put(PbsTokenKind.ERROR, PbsSemanticKind.ENUM);
kinds.put(PbsTokenKind.CALLBACK, PbsSemanticKind.CALLBACK);
kinds.put(PbsTokenKind.GLOBAL, PbsSemanticKind.GLOBAL);
kinds.put(PbsTokenKind.CONST, PbsSemanticKind.CONST);
kinds.put(PbsTokenKind.CTOR, PbsSemanticKind.CONSTRUCTOR);
kinds.put(PbsTokenKind.BUILTIN, PbsSemanticKind.BUILTIN_TYPE);
return Map.copyOf(kinds);
}
private boolean isTypeReferenceContext(final PbsTokenKind previousKind) {
if (previousKind == null) {
return false;
}
if (previousKind != PbsTokenKind.COLON
&& previousKind != PbsTokenKind.ARROW
&& previousKind != PbsTokenKind.AS
&& previousKind != PbsTokenKind.IMPLEMENTS) {
return false;
}
return true;
}
private PbsTokenKind previousSignificantKind(
final ReadOnlyList<PbsToken> tokens,
final int index) {
for (int cursor = index - 1; cursor >= 0; cursor -= 1) {
final PbsTokenKind kind = tokens.get(cursor).kind();
if (kind == PbsTokenKind.COMMENT || kind == PbsTokenKind.EOF) {
continue;
}
return kind;
}
return null;
}
private PbsTokenKind nextSignificantKind(
final ReadOnlyList<PbsToken> tokens,
final int index) {
final Integer cursor = nextSignificantIndex(tokens, index);
return cursor == null ? null : tokens.get(cursor).kind();
}
private Integer nextSignificantIndex(
final ReadOnlyList<PbsToken> tokens,
final int index) {
for (int cursor = index + 1; cursor < tokens.size(); cursor += 1) {
final PbsTokenKind kind = tokens.get(cursor).kind();
if (kind == PbsTokenKind.COMMENT || kind == PbsTokenKind.EOF) {
continue;
}
return cursor;
}
return null;
}
private boolean isLifecycleToken(
final ReadOnlyList<PbsToken> tokens,
final int index,
final PbsToken token) {
if (!"Init".equals(token.lexeme()) && !"Frame".equals(token.lexeme())) {
return false;
}
if (index == 0 || index + 1 >= tokens.size()) {
return false;
}
return tokens.get(index - 1).kind() == PbsTokenKind.LEFT_BRACKET
&& tokens.get(index + 1).kind() == PbsTokenKind.RIGHT_BRACKET;
}
}

View File

@ -22,6 +22,7 @@ public enum PbsSemanticKind {
HOST("pbs-host"),
BUILTIN_TYPE("pbs-builtin-type"),
SERVICE("pbs-service"),
ASSET("pbs-asset"),
ERROR("pbs-error"),
ENUM("pbs-enum"),
CALLBACK("pbs-callback"),
@ -51,12 +52,13 @@ public enum PbsSemanticKind {
case COMMENT -> COMMENT;
case STRING_LITERAL -> STRING;
case INT_LITERAL, FLOAT_LITERAL, BOUNDED_LITERAL -> NUMBER;
case TRUE, FALSE, NONE -> LITERAL;
case TRUE, FALSE, NONE, SOME, OK, ERR -> LITERAL;
case VOID, OPTIONAL, RESULT -> BUILTIN_TYPE;
case IMPORT, FROM, AS, SERVICE, HOST, FN, APPLY, BIND, NEW, IMPLEMENTS, USING, CTOR,
DECLARE, LET, CONST, GLOBAL, STRUCT, CONTRACT, ERROR, ENUM, CALLBACK, BUILTIN,
SELF, THIS, PUB, MUT, MOD, TYPE, IF, ELSE, SWITCH, DEFAULT, FOR, UNTIL, STEP,
WHILE, BREAK, CONTINUE, RETURN, VOID, OPTIONAL, RESULT, SOME, OK, ERR, HANDLE,
AND, OR, NOT, SPAWN, YIELD, SLEEP, MATCH -> KEYWORD;
WHILE, BREAK, CONTINUE, RETURN, HANDLE, AND, OR, NOT, SPAWN, YIELD, SLEEP,
MATCH -> KEYWORD;
case PLUS, MINUS, STAR, SLASH, PERCENT, BANG, PLUS_EQUAL, MINUS_EQUAL, STAR_EQUAL,
SLASH_EQUAL, PERCENT_EQUAL, BANG_EQUAL, EQUAL, EQUAL_EQUAL, LESS, LESS_EQUAL,
GREATER, GREATER_EQUAL, AND_AND, OR_OR, ARROW, QUESTION -> OPERATOR;

View File

@ -0,0 +1,30 @@
package p.studio.compiler.pbs.semantics;
import java.util.Objects;
public record PbsEditorialCompletionCandidate(
String label,
PbsEditorialSymbolKind kind,
String detail,
String origin) {
public PbsEditorialCompletionCandidate {
label = requireText(label, "label");
kind = Objects.requireNonNull(kind, "kind");
detail = normalize(detail);
origin = normalize(origin);
}
private static String requireText(
final String value,
final String field) {
final String candidate = normalize(value);
if (candidate.isEmpty()) {
throw new IllegalArgumentException(field + " must not be blank");
}
return candidate;
}
private static String normalize(final String value) {
return value == null ? "" : value.trim();
}
}

View File

@ -0,0 +1,35 @@
package p.studio.compiler.pbs.semantics;
import java.util.List;
import java.util.Objects;
public record PbsEditorialResolvedSymbol(
String displayName,
PbsEditorialSymbolKind kind,
String detail,
String origin,
List<PbsEditorialSignature> signatures,
String documentation) {
public PbsEditorialResolvedSymbol {
displayName = requireText(displayName, "displayName");
kind = Objects.requireNonNull(kind, "kind");
detail = normalize(detail);
origin = normalize(origin);
signatures = List.copyOf(Objects.requireNonNull(signatures, "signatures"));
documentation = normalize(documentation);
}
private static String requireText(
final String value,
final String field) {
final String candidate = normalize(value);
if (candidate.isEmpty()) {
throw new IllegalArgumentException(field + " must not be blank");
}
return candidate;
}
private static String normalize(final String value) {
return value == null ? "" : value.trim();
}
}

View File

@ -0,0 +1,27 @@
package p.studio.compiler.pbs.semantics;
import java.util.List;
import java.util.Objects;
public record PbsEditorialSignature(
String callableName,
String label,
List<String> parameterLabels,
String returnLabel) {
public PbsEditorialSignature {
callableName = requireText(callableName, "callableName");
label = requireText(label, "label");
parameterLabels = List.copyOf(Objects.requireNonNull(parameterLabels, "parameterLabels"));
returnLabel = requireText(returnLabel, "returnLabel");
}
private static String requireText(
final String value,
final String field) {
final String candidate = Objects.requireNonNull(value, field).trim();
if (candidate.isEmpty()) {
throw new IllegalArgumentException(field + " must not be blank");
}
return candidate;
}
}

View File

@ -0,0 +1,22 @@
package p.studio.compiler.pbs.semantics;
import java.util.List;
import java.util.Objects;
public record PbsEditorialSignatureHelp(
List<PbsEditorialSignature> signatures,
int activeSignature,
int activeParameter) {
public PbsEditorialSignatureHelp {
signatures = List.copyOf(Objects.requireNonNull(signatures, "signatures"));
if (signatures.isEmpty()) {
throw new IllegalArgumentException("signatures must not be empty");
}
if (activeSignature < 0 || activeSignature >= signatures.size()) {
throw new IllegalArgumentException("activeSignature must reference one of the provided signatures");
}
if (activeParameter < 0) {
throw new IllegalArgumentException("activeParameter must be >= 0");
}
}
}

View File

@ -0,0 +1,21 @@
package p.studio.compiler.pbs.semantics;
public enum PbsEditorialSymbolKind {
KEYWORD,
LOCAL,
PARAMETER,
FIELD,
FUNCTION,
METHOD,
CONSTRUCTOR,
STRUCT,
BUILTIN_TYPE,
SERVICE,
HOST,
CONTRACT,
CALLBACK,
ENUM,
ERROR,
GLOBAL,
CONST
}

View File

@ -584,5 +584,9 @@ final class PbsFlowSemanticSupport {
final var symbol = names.get(name);
return symbol != null && symbol.mutable();
}
Map<String, LocalSymbol> entries() {
return Map.copyOf(names);
}
}
}

View File

@ -133,12 +133,14 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
assembly.parsedSourceFiles(),
assembly.moduleTable());
final var flowSemanticsValidator = new PbsFlowSemanticsValidator();
final Map<FileId, p.studio.compiler.pbs.ast.PbsAst.File> astByFile = new LinkedHashMap<>();
final Map<FileId, ReadOnlyList<p.studio.compiler.pbs.ast.PbsAst.TopDecl>> supplementalTopDeclsByFile = new LinkedHashMap<>();
final Map<FileId, ReadOnlyList<PbsInlineHintSurface>> inlineHintsByFile = new LinkedHashMap<>();
for (final var entry : importedSemanticContexts.entrySet()) {
supplementalTopDeclsByFile.put(entry.getKey(), entry.getValue().supplementalTopDecls());
}
for (final var parsedSourceFile : assembly.parsedSourceFiles()) {
astByFile.put(parsedSourceFile.fileId(), parsedSourceFile.ast());
final var importedSemanticContext = importedSemanticContexts.getOrDefault(
parsedSourceFile.fileId(),
PbsImportedSemanticContext.empty());
@ -152,11 +154,13 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
}
}
return new PbsSemanticReadSurface(
Map.copyOf(astByFile),
Map.copyOf(supplementalTopDeclsByFile),
Map.copyOf(inlineHintsByFile));
}
public record PbsSemanticReadSurface(
Map<FileId, p.studio.compiler.pbs.ast.PbsAst.File> astByFile,
Map<FileId, ReadOnlyList<p.studio.compiler.pbs.ast.PbsAst.TopDecl>> supplementalTopDeclsByFile,
Map<FileId, ReadOnlyList<PbsInlineHintSurface>> inlineHintsByFile) {
}

View File

@ -1,111 +0,0 @@
.editor-workspace-code-area-type-pbs {
-fx-highlight-fill: #2d4f6b;
}
.editor-workspace-code-area-type-pbs .text {
-fx-fill: #d9e2ec;
}
.editor-workspace-code-area-type-pbs .lineno {
-fx-text-fill: #6f8193;
}
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-keyword {
-fx-fill: #569cd6;
}
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-lifecycle {
-fx-fill: #ef50c0;
}
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-function {
-fx-fill: #f2c14e;
}
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-method {
-fx-fill: #f2c14e;
}
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-constructor {
-fx-fill: #ecdcaa;
}
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-struct {
-fx-fill: #4ec9b0;
}
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-contract {
-fx-fill: #78dce8;
}
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-host {
-fx-fill: #b7a2fa;
}
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-builtin-type {
-fx-fill: #8be9fd;
}
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-service {
-fx-fill: #b7a2fa;
}
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-error {
-fx-fill: #ff5b5b;
}
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-enum {
-fx-fill: #56cfe1;
}
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-callback {
-fx-fill: #b7a2fa;
}
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-global {
-fx-fill: #f790fc;
-fx-font-style: italic;
}
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-const {
-fx-fill: #f78c6c;
}
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-implements {
-fx-fill: #a1c181;
}
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-string {
-fx-fill: #00c088;
}
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-number {
-fx-fill: #ff90b0;
}
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-comment {
-fx-fill: #c090b0;
-fx-font-style: italic;
}
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-literal {
-fx-fill: #4fc1ff;
}
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-operator {
-fx-fill: #d4d4d4;
}
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-punctuation {
-fx-fill: #d4d4d4;
}
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-identifier {
-fx-fill: #d4d4d4;
}
.editor-workspace-status-chip-type-pbs {
-fx-background-color: #2c2140;
-fx-border-color: #a47dff;
-fx-text-fill: #efe6ff;
}

View File

@ -1,13 +1,14 @@
package p.studio.compiler.pbs;
import org.junit.jupiter.api.Test;
import p.studio.compiler.PBSFrontendHostProjections;
import p.studio.compiler.PBSDefinitions;
import p.studio.compiler.models.FrontendSemanticToken;
import java.net.URL;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
final class PbsSemanticPresentationContractTest {
@ -18,16 +19,128 @@ final class PbsSemanticPresentationContractTest {
assertFalse(presentation.semanticKeys().isEmpty());
assertEquals(PbsSemanticKind.semanticKeys(), presentation.semanticKeys());
assertEquals(1, presentation.resources().size());
assertEquals("/themes/pbs/semantic-highlighting.css", presentation.resources().getFirst());
assertEquals("pbs-default", presentation.defaultThemeId());
assertEquals(1, presentation.themes().size());
assertEquals("PBS Default", presentation.defaultTheme().displayName());
assertEquals("#d9e2ec", presentation.defaultTheme().editorPalette().baseForeground());
assertEquals("#7fb8ff", presentation.defaultTheme().requireTokenStyle("pbs-keyword").foreground());
assertEquals("#d8b86a", presentation.defaultTheme().requireTokenStyle("pbs-string").foreground());
assertEquals("#ff8cc6", presentation.defaultTheme().requireTokenStyle("pbs-number").foreground());
assertEquals("#7fb8ff", presentation.defaultTheme().requireTokenStyle("pbs-literal").foreground());
assertEquals("#7ee787", presentation.defaultTheme().requireTokenStyle("pbs-function").foreground());
assertEquals("#7ee787", presentation.defaultTheme().requireTokenStyle("pbs-method").foreground());
assertEquals("#7ee787", presentation.defaultTheme().requireTokenStyle("pbs-constructor").foreground());
assertEquals("#56d4dd", presentation.defaultTheme().requireTokenStyle("pbs-struct").foreground());
assertTrue(presentation.defaultTheme().requireTokenStyle("pbs-struct").italic());
assertEquals("#56d4dd", presentation.defaultTheme().requireTokenStyle("pbs-builtin-type").foreground());
assertFalse(presentation.defaultTheme().requireTokenStyle("pbs-builtin-type").italic());
assertEquals("#ff8e7a", presentation.defaultTheme().requireTokenStyle("pbs-service").foreground());
assertEquals("#c297ff", presentation.defaultTheme().requireTokenStyle("pbs-asset").foreground());
assertTrue(presentation.defaultTheme().requireTokenStyle("pbs-comment").italic());
assertEquals(PBSFrontendHostProjections.VSCODE_HOST_ID, presentation.requireHostProjection("vscode").hostId());
}
@Test
void shouldPublishResolvableSemanticPresentationResources() {
final var resourcePath = PBSDefinitions.PBS.getSemanticPresentation().resources().getFirst();
final URL resource = PBSDefinitions.class.getResource(resourcePath);
void shouldCoverTheWholePbsSemanticVocabularyInTheDefaultTheme() {
final var presentation = PBSDefinitions.PBS.getSemanticPresentation();
final var defaultTheme = presentation.defaultTheme();
assertNotNull(resource, resourcePath);
assertTrue(resource.toExternalForm().endsWith("themes/pbs/semantic-highlighting.css"));
assertEquals(
PbsSemanticKind.semanticKeys(),
defaultTheme.tokenStyles().stream().map(tokenStyle -> tokenStyle.semanticKey()).toList());
}
@Test
void shouldCoverTheWholePbsSemanticVocabularyInTheVscodeProjection() {
final var presentation = PBSDefinitions.PBS.getSemanticPresentation();
final var vscodeProjection = presentation.requireHostProjection(PBSFrontendHostProjections.VSCODE_HOST_ID);
assertEquals(
PbsSemanticKind.semanticKeys(),
vscodeProjection.tokenProjections().stream().map(tokenProjection -> tokenProjection.semanticKey()).toList());
assertEquals("keyword", vscodeProjection.requireProjection(PbsSemanticKind.KEYWORD.semanticKey()).hostTokenType());
assertEquals("type", vscodeProjection.requireProjection(PbsSemanticKind.STRUCT.semanticKey()).fallbackTokenType());
assertEquals("variable", vscodeProjection.requireProjection(PbsSemanticKind.ASSET.semanticKey()).hostTokenType());
assertEquals(
java.util.List.of("readonly"),
vscodeProjection.requireProjection(PbsSemanticKind.GLOBAL.semanticKey()).hostTokenModifiers());
}
@Test
void shouldPublishPbsOwnedSemanticTokenProvider() {
final List<FrontendSemanticToken> semanticTokens = PBSDefinitions.PBS.getSemanticTokenProvider().tokenize("""
struct Vec2 {}
service Log {}
host Input {}
enum ResultKind { Ok = 1; }
fn main() -> void {
const answer: int = 42;
let value = new Vec2();
value.length();
Log.info();
Input.read();
let kind: ResultKind;
}
""");
assertTrue(semanticTokens.stream().anyMatch(token -> token.semanticKey().equals(PbsSemanticKind.BUILTIN_TYPE.semanticKey())));
assertTrue(semanticTokens.stream().anyMatch(token -> token.semanticKey().equals(PbsSemanticKind.FUNCTION.semanticKey())));
assertTrue(semanticTokens.stream().anyMatch(token -> token.semanticKey().equals(PbsSemanticKind.CONST.semanticKey())));
assertTrue(semanticTokens.stream().anyMatch(token -> token.semanticKey().equals(PbsSemanticKind.BUILTIN_TYPE.semanticKey())));
assertTrue(semanticTokens.stream().anyMatch(token -> token.semanticKey().equals(PbsSemanticKind.CONSTRUCTOR.semanticKey())));
assertTrue(semanticTokens.stream().anyMatch(token -> token.semanticKey().equals(PbsSemanticKind.METHOD.semanticKey())));
assertTrue(semanticTokens.stream().anyMatch(token -> token.semanticKey().equals(PbsSemanticKind.SERVICE.semanticKey())));
assertTrue(semanticTokens.stream().anyMatch(token -> token.semanticKey().equals(PbsSemanticKind.HOST.semanticKey())));
assertTrue(semanticTokens.stream().anyMatch(token -> token.semanticKey().equals(PbsSemanticKind.ENUM.semanticKey())));
}
@Test
void shouldClassifyStdlibImportsUsingTheirPublishedBarrelKinds() {
final List<FrontendSemanticToken> semanticTokens = PBSDefinitions.PBS.getSemanticTokenProvider().tokenize("""
import { Log } from @sdk:log;
import { Composer } from @sdk:composer;
import { Color as Paint } from @core:color;
fn main() -> void {
Log.info("hello");
Composer.emit_sprite(0, 0, 0, 0, 0, 0, false, false, 0);
let paint: Paint;
}
""");
assertTrue(semanticTokens.stream().anyMatch(token -> token.semanticKey().equals(PbsSemanticKind.SERVICE.semanticKey())));
assertTrue(semanticTokens.stream().anyMatch(token -> token.semanticKey().equals(PbsSemanticKind.BUILTIN_TYPE.semanticKey())));
}
@Test
void shouldClassifyAssetRootedAliasesUsingDedicatedSemanticKind() {
final List<FrontendSemanticToken> semanticTokens = PBSDefinitions.PBS.getSemanticTokenProvider().tokenize("""
import { Assets } from @sdk:asset;
fn main() -> void {
Assets.load(assets.ui.atlas2, 3);
}
""");
final long assetTokenCount = semanticTokens.stream()
.filter(token -> token.semanticKey().equals(PbsSemanticKind.ASSET.semanticKey()))
.count();
assertEquals(3, assetTokenCount);
}
@Test
void shouldClassifyBuiltinTypesExposedByImportedStdlibModules() {
final List<FrontendSemanticToken> semanticTokens = PBSDefinitions.PBS.getSemanticTokenProvider().tokenize("""
import { Input } from @sdk:input;
fn main() -> void {
let touch : InputTouch = Input.touch();
let pad : InputPad = Input.pad();
}
""");
assertTrue(semanticTokens.stream().filter(token -> token.semanticKey().equals(PbsSemanticKind.BUILTIN_TYPE.semanticKey())).count() >= 2);
}
}

View File

@ -0,0 +1,215 @@
package p.studio.compiler.pbs.semantics;
import org.junit.jupiter.api.Test;
import p.studio.compiler.pbs.ast.PbsAst;
import p.studio.compiler.pbs.lexer.PbsLexer;
import p.studio.compiler.pbs.parser.PbsParser;
import p.studio.compiler.source.diagnostics.DiagnosticSink;
import p.studio.compiler.source.identifiers.FileId;
import p.studio.utilities.structures.ReadOnlyList;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
final class PbsEditorialSupportServiceTest {
private final PbsEditorialSupportService service = new PbsEditorialSupportService();
@Test
void shouldResolveGeneralCompletionAndHoverAcrossLocalAndImportedSymbols() {
final var source = """
import { Log } from @sdk:log;
fn helper(value: int) -> int {
let total = value;
Log.info("hello");
return total;
}
""";
final var ast = parseOrdinary(source);
final var supplementalTopDecls = supplementalTopDecls("""
declare service Log {
fn info(message: str) -> void {
return;
}
}
""");
final var completions = service.completion(
source,
ast,
supplementalTopDecls,
source.indexOf("return total"));
assertTrue(completions.stream().anyMatch(candidate ->
candidate.label().equals("fn") && candidate.kind() == PbsEditorialSymbolKind.KEYWORD));
assertTrue(completions.stream().anyMatch(candidate ->
candidate.label().equals("value") && candidate.kind() == PbsEditorialSymbolKind.PARAMETER));
assertTrue(completions.stream().anyMatch(candidate ->
candidate.label().equals("total") && candidate.kind() == PbsEditorialSymbolKind.LOCAL));
assertTrue(completions.stream().anyMatch(candidate ->
candidate.label().equals("Log") && candidate.kind() == PbsEditorialSymbolKind.SERVICE));
assertTrue(completions.stream().anyMatch(candidate ->
candidate.label().equals("helper") && candidate.kind() == PbsEditorialSymbolKind.FUNCTION));
final var parameterHover = requireHover(source, ast, supplementalTopDecls, "value;");
assertEquals(PbsEditorialSymbolKind.PARAMETER, parameterHover.kind());
assertEquals("int", parameterHover.detail());
final var importedHover = requireHover(source, ast, supplementalTopDecls, "Log.info");
assertEquals(PbsEditorialSymbolKind.SERVICE, importedHover.kind());
assertEquals("@sdk:log", importedHover.origin());
}
@Test
void shouldResolveMemberCompletionAndHoverForImportedHostAndBuiltinTypes() {
final var source = """
import { Input } from @sdk:input;
fn main() -> void {
let touch: InputTouch = Input.touch();
Input.touch();
touch.x_axis();
}
""";
final var ast = parseOrdinary(source);
final var supplementalTopDecls = supplementalTopDecls("""
declare builtin type InputTouch(
pub x: int
) {
fn x_axis() -> int;
}
declare host Input {
fn touch() -> InputTouch;
fn pad() -> InputPad;
}
declare builtin type InputPad(
pub id: int
) {
fn strength() -> int;
}
""");
final var hostMembers = service.completion(
source,
ast,
supplementalTopDecls,
source.indexOf("touch();"));
assertTrue(hostMembers.stream().anyMatch(candidate ->
candidate.label().equals("touch") && candidate.kind() == PbsEditorialSymbolKind.METHOD));
assertTrue(hostMembers.stream().anyMatch(candidate ->
candidate.label().equals("pad") && candidate.kind() == PbsEditorialSymbolKind.METHOD));
final var builtinMembers = service.completion(
source,
ast,
supplementalTopDecls,
source.indexOf("x_axis();"));
assertTrue(builtinMembers.stream().anyMatch(candidate ->
candidate.label().equals("x") && candidate.kind() == PbsEditorialSymbolKind.FIELD));
assertTrue(builtinMembers.stream().anyMatch(candidate ->
candidate.label().equals("x_axis") && candidate.kind() == PbsEditorialSymbolKind.METHOD));
final var builtinTypeHover = requireHover(source, ast, supplementalTopDecls, "InputTouch");
assertEquals(PbsEditorialSymbolKind.BUILTIN_TYPE, builtinTypeHover.kind());
final var hostMethodHover = requireHover(source, ast, supplementalTopDecls, "touch();");
assertEquals(PbsEditorialSymbolKind.METHOD, hostMethodHover.kind());
assertEquals("Input", hostMethodHover.origin());
}
@Test
void shouldResolveSignatureHelpForFunctionMethodAndConstructorCalls() {
final var source = """
declare struct Vec() {
fn blend(dx: int, dy: int) -> int { return dx; }
}
fn mix(left: int, right: int) -> int { return left; }
fn main(vec: Vec) -> void {
mix(1, 2);
vec.blend(1, 2);
new Vec();
}
""";
final var ast = parseOrdinary(source);
final var functionHelp = service.signatureHelp(
source,
ast,
ReadOnlyList.empty(),
source.indexOf("2);"));
assertTrue(functionHelp.isPresent());
assertEquals("mix(left: int, right: int) -> int", functionHelp.orElseThrow().signatures().getFirst().label());
assertEquals(1, functionHelp.orElseThrow().activeParameter());
final var methodHelp = service.signatureHelp(
source,
ast,
ReadOnlyList.empty(),
source.lastIndexOf("2);"));
assertTrue(methodHelp.isPresent());
assertEquals("blend(arg0: int, arg1: int) -> int", methodHelp.orElseThrow().signatures().getFirst().label());
assertEquals(1, methodHelp.orElseThrow().activeParameter());
final var constructorHelp = service.signatureHelp(
source,
ast,
ReadOnlyList.empty(),
source.indexOf("Vec();") + 4);
assertTrue(constructorHelp.isPresent());
assertEquals("Vec()", constructorHelp.orElseThrow().signatures().getFirst().label());
final var constructorHover = requireHover(source, ast, ReadOnlyList.empty(), "Vec();");
assertEquals(PbsEditorialSymbolKind.CONSTRUCTOR, constructorHover.kind());
assertEquals("Vec()", constructorHover.signatures().getFirst().label());
}
private PbsEditorialResolvedSymbol requireHover(
final String source,
final PbsAst.File ast,
final ReadOnlyList<PbsAst.TopDecl> supplementalTopDecls,
final String needle) {
final int offset = source.indexOf(needle);
assertTrue(offset >= 0, "Expected to find needle: " + needle);
return service.hover(source, ast, supplementalTopDecls, offset)
.orElseThrow();
}
private ReadOnlyList<PbsAst.TopDecl> supplementalTopDecls(final String... interfaceModuleSources) {
final ArrayList<PbsAst.TopDecl> topDecls = new ArrayList<>();
int fileNumber = 100;
for (final var source : interfaceModuleSources) {
topDecls.addAll(parseInterface(source, new FileId(fileNumber)).topDecls().asList());
fileNumber += 1;
}
return ReadOnlyList.wrap(topDecls);
}
private PbsAst.File parseOrdinary(final String source) {
return parse(source, new FileId(0), PbsParser.ParseMode.ORDINARY);
}
private PbsAst.File parseInterface(
final String source,
final FileId fileId) {
return parse(source, fileId, PbsParser.ParseMode.INTERFACE_MODULE);
}
private PbsAst.File parse(
final String source,
final FileId fileId,
final PbsParser.ParseMode parseMode) {
final var diagnostics = DiagnosticSink.empty();
final var tokens = PbsLexer.lex(source, fileId, diagnostics);
final var ast = PbsParser.parse(tokens, fileId, diagnostics, parseMode);
assertFalse(diagnostics.hasErrors(), () -> "Unexpected parse errors: " + diagnostics.stream().toList());
return ast;
}
}

View File

@ -57,7 +57,7 @@ public class BuilderPipelineService {
public AnalysisSnapshot analyze(
final BuilderPipelineContext ctx,
final LogAggregator logs) {
final var diagnostics = run(ctx, logs, analyses, new ArrayList<>());
final var diagnostics = run(ctx, logs, analyses, new ArrayList<>(), false);
return new AnalysisSnapshot(
ctx.resolvedWorkspace.frontendSpec(),
diagnostics,
@ -70,7 +70,10 @@ public class BuilderPipelineService {
final BuilderPipelineContext ctx,
final LogAggregator logs) {
final var analysisSnapshot = this.analyze(ctx, logs);
final var diagnostics = run(ctx, logs, compile, new ArrayList<>(analysisSnapshot.diagnostics()));
if (hasErrors(analysisSnapshot.diagnostics())) {
throw new BuildException("issues found on pipeline stage: analyze");
}
final var diagnostics = run(ctx, logs, compile, new ArrayList<>(analysisSnapshot.diagnostics()), true);
return new CompileResult(
analysisSnapshot,
diagnostics,
@ -84,7 +87,7 @@ public class BuilderPipelineService {
final BuilderPipelineContext ctx,
final LogAggregator logs) {
final var compileResult = this.compile(ctx, logs);
final var diagnostics = run(ctx, logs, build, new ArrayList<>(compileResult.diagnostics()));
final var diagnostics = run(ctx, logs, build, new ArrayList<>(compileResult.diagnostics()), true);
return new BuildResult(
compileResult,
diagnostics,
@ -95,19 +98,32 @@ public class BuilderPipelineService {
final BuilderPipelineContext ctx,
final LogAggregator logs,
final List<PipelineStage> stages,
final List<BuildingIssue> diagnostics) {
final List<BuildingIssue> diagnostics,
final boolean failFast) {
for (final var builderPipelineStage : stages) {
final var issues = builderPipelineStage.run(ctx, logs);
diagnostics.addAll(issues.asCollection());
printIssues(issues, logs);
if (issues.hasErrors()) {
throw new BuildException("issues found on pipeline stage: " + builderPipelineStage.getClass().getSimpleName());
if (failFast) {
throw new BuildException("issues found on pipeline stage: " + builderPipelineStage.getClass().getSimpleName());
}
break;
}
}
logs.using(log).info("builder pipeline completed successfully");
return List.copyOf(diagnostics);
}
private boolean hasErrors(final List<BuildingIssue> diagnostics) {
for (final var diagnostic : diagnostics) {
if (diagnostic.isError()) {
return true;
}
}
return false;
}
private void printIssues(
final ReadOnlyCollection<BuildingIssue> issues,
final LogAggregator logs) {

View File

@ -48,6 +48,11 @@ public class FrontendPhasePipelineStage implements PipelineStage {
final var span = diagnostic.getSpan();
issues.report(builder -> builder
.error(diagnostic.getSeverity().isError())
.phase(diagnostic.getPhase().id())
.code(diagnostic.getCode())
.fileId(span.getFileId().isNone() ? null : span.getFileId().getId())
.start(safeToInt(span.getStart()))
.end(safeToInt(span.getEnd()))
.message("[BUILD] %s :: %s @ %s:[%d,%d)".formatted(
diagnostic.getCode(),
diagnostic.getMessage(),
@ -56,4 +61,8 @@ public class FrontendPhasePipelineStage implements PipelineStage {
span.getEnd())));
}
}
private int safeToInt(final long value) {
return value > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) Math.max(0L, value);
}
}

View File

@ -3,12 +3,15 @@ package p.studio.compiler.integration;
import org.junit.jupiter.api.Test;
import p.studio.compiler.messages.BuilderPipelineConfig;
import p.studio.compiler.models.BuilderPipelineContext;
import p.studio.compiler.source.identifiers.FileId;
import p.studio.compiler.utilities.SourceProviderFactory;
import p.studio.compiler.workspaces.BuilderPipelineService;
import p.studio.utilities.logs.LogAggregator;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
@ -32,6 +35,36 @@ class MainProjectPipelineIntegrationTest {
assertFalse(Files.exists(outputPath), "analyze must not write output: " + outputPath);
}
@Test
void analyzeShouldReturnStructuredDiagnosticsForOverlayDocumentWithoutThrowing() throws IOException {
final var projectRoot = projectRoot();
final var documentPath = projectRoot.resolve("src").resolve("main.pbs").toAbsolutePath().normalize();
final var logs = bufferedLogs();
final var invalidOverlay = """
fn frame() -> void {
let broken: int = ;
}
""";
final var context = BuilderPipelineContext.fromConfig(new BuilderPipelineConfig(
false,
projectRoot.toString(),
"core-v1",
SourceProviderFactory.overlayUtf8(Map.of(documentPath, invalidOverlay))));
final var snapshot = assertDoesNotThrow(
() -> BuilderPipelineService.INSTANCE.analyze(context, logs),
() -> "analyze unexpectedly failed for overlay " + documentPath);
final var documentDiagnostics = snapshot.diagnostics().stream()
.filter(issue -> issue.getFileId() != null)
.filter(issue -> issue.getFileId() >= 0 && issue.getFileId() < snapshot.fileTable().size())
.filter(issue -> snapshot.fileTable().get(new FileId(issue.getFileId())).getCanonPath().toAbsolutePath().normalize().equals(documentPath))
.toList();
assertFalse(documentDiagnostics.isEmpty(), "overlay analysis must surface diagnostics for " + documentPath);
assertTrue(documentDiagnostics.stream().anyMatch(issue -> issue.isError() && issue.getCode() != null && issue.getStart() != null && issue.getEnd() != null));
}
@Test
void compileShouldProduceInMemoryBytecodeWithoutWritingProgramBytecode() throws IOException {
final var projectRoot = projectRoot();

View File

@ -0,0 +1,33 @@
package p.studio.compiler.models;
import java.util.Objects;
public record FrontendEditorPaletteSpec(
String baseForeground,
String selectionBackground,
String activeHighlightBackground,
String lineNumberForeground,
String statusChipBackground,
String statusChipBorder,
String statusChipForeground) {
public FrontendEditorPaletteSpec {
baseForeground = requireColor(baseForeground, "baseForeground");
selectionBackground = requireColor(selectionBackground, "selectionBackground");
activeHighlightBackground = requireColor(activeHighlightBackground, "activeHighlightBackground");
lineNumberForeground = requireColor(lineNumberForeground, "lineNumberForeground");
statusChipBackground = requireColor(statusChipBackground, "statusChipBackground");
statusChipBorder = requireColor(statusChipBorder, "statusChipBorder");
statusChipForeground = requireColor(statusChipForeground, "statusChipForeground");
}
private static String requireColor(
final String value,
final String field) {
final String candidate = Objects.requireNonNull(value, field).trim();
if (candidate.isEmpty()) {
throw new IllegalArgumentException(field + " must not be blank");
}
return candidate;
}
}

View File

@ -0,0 +1,38 @@
package p.studio.compiler.models;
import java.util.List;
import java.util.Objects;
public record FrontendHostProjectionEntrySpec(
String semanticKey,
String hostTokenType,
List<String> hostTokenModifiers,
String fallbackTokenType,
List<String> fallbackTokenModifiers) {
public FrontendHostProjectionEntrySpec {
semanticKey = requireText(semanticKey, "semanticKey");
hostTokenType = requireText(hostTokenType, "hostTokenType");
hostTokenModifiers = normalizeList(hostTokenModifiers, "hostTokenModifiers");
fallbackTokenType = requireText(fallbackTokenType, "fallbackTokenType");
fallbackTokenModifiers = normalizeList(fallbackTokenModifiers, "fallbackTokenModifiers");
}
private static List<String> normalizeList(
final List<String> values,
final String field) {
return List.copyOf(Objects.requireNonNull(values, field).stream()
.map(value -> requireText(value, field + " entry"))
.toList());
}
private static String requireText(
final String value,
final String field) {
final String candidate = Objects.requireNonNull(value, field).trim();
if (candidate.isEmpty()) {
throw new IllegalArgumentException(field + " must not be blank");
}
return candidate;
}
}

View File

@ -0,0 +1,48 @@
package p.studio.compiler.models;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
public record FrontendHostProjectionSpec(
String hostId,
List<FrontendHostProjectionEntrySpec> tokenProjections) {
public FrontendHostProjectionSpec {
hostId = requireText(hostId, "hostId");
tokenProjections = List.copyOf(Objects.requireNonNull(tokenProjections, "tokenProjections"));
if (tokenProjections.isEmpty()) {
throw new IllegalArgumentException("tokenProjections must not be empty");
}
final Map<String, FrontendHostProjectionEntrySpec> bySemanticKey = new LinkedHashMap<>();
for (final FrontendHostProjectionEntrySpec tokenProjection : tokenProjections) {
final FrontendHostProjectionEntrySpec previous = bySemanticKey.put(tokenProjection.semanticKey(), tokenProjection);
if (previous != null) {
throw new IllegalArgumentException("duplicate host projection for semantic key: " + tokenProjection.semanticKey());
}
}
}
public Optional<FrontendHostProjectionEntrySpec> projectionFor(final String semanticKey) {
return tokenProjections.stream()
.filter(tokenProjection -> tokenProjection.semanticKey().equals(semanticKey))
.findFirst();
}
public FrontendHostProjectionEntrySpec requireProjection(final String semanticKey) {
return projectionFor(semanticKey)
.orElseThrow(() -> new IllegalArgumentException("missing host projection for semantic key: " + semanticKey));
}
private static String requireText(
final String value,
final String field) {
final String candidate = Objects.requireNonNull(value, field).trim();
if (candidate.isEmpty()) {
throw new IllegalArgumentException(field + " must not be blank");
}
return candidate;
}
}

View File

@ -4,15 +4,117 @@ import java.util.List;
import java.util.Objects;
public record FrontendSemanticPresentationSpec(
List<String> semanticKeys,
List<String> resources) {
List<FrontendVisualThemeSpec> themes,
String defaultThemeId,
List<FrontendHostProjectionSpec> hostProjections) {
public FrontendSemanticPresentationSpec {
semanticKeys = List.copyOf(Objects.requireNonNull(semanticKeys, "semanticKeys"));
resources = List.copyOf(Objects.requireNonNull(resources, "resources"));
themes = List.copyOf(Objects.requireNonNull(themes, "themes"));
defaultThemeId = Objects.requireNonNull(defaultThemeId, "defaultThemeId").trim();
hostProjections = List.copyOf(Objects.requireNonNull(hostProjections, "hostProjections"));
if (themes.isEmpty()) {
if (!defaultThemeId.isEmpty()) {
throw new IllegalArgumentException("defaultThemeId must be blank when no themes are declared");
}
if (!hostProjections.isEmpty()) {
throw new IllegalArgumentException("hostProjections must be empty when no themes are declared");
}
} else {
defaultThemeId = requireText(defaultThemeId, "defaultThemeId");
final String resolvedDefaultThemeId = defaultThemeId;
if (themes.stream().map(FrontendVisualThemeSpec::themeId).noneMatch(resolvedDefaultThemeId::equals)) {
throw new IllegalArgumentException("defaultThemeId must reference one of the declared themes");
}
final List<String> derivedSemanticKeys = themes.stream()
.filter(theme -> theme.themeId().equals(resolvedDefaultThemeId))
.findFirst()
.orElseThrow(() -> new IllegalStateException("defaultThemeId must resolve to a declared theme"))
.tokenStyles()
.stream()
.map(FrontendTokenStyleSpec::semanticKey)
.toList();
for (final FrontendVisualThemeSpec theme : themes) {
final List<String> themeSemanticKeys = theme.tokenStyles().stream()
.map(FrontendTokenStyleSpec::semanticKey)
.toList();
if (!themeSemanticKeys.equals(derivedSemanticKeys)) {
throw new IllegalArgumentException(
"theme '" + theme.themeId() + "' must publish the same semantic keys as the default theme");
}
}
final var hostIds = new java.util.LinkedHashSet<String>();
for (final FrontendHostProjectionSpec hostProjection : hostProjections) {
if (!hostIds.add(hostProjection.hostId())) {
throw new IllegalArgumentException(
"duplicate host projection for hostId: " + hostProjection.hostId());
}
final List<String> projectedSemanticKeys = hostProjection.tokenProjections().stream()
.map(FrontendHostProjectionEntrySpec::semanticKey)
.toList();
if (!projectedSemanticKeys.equals(derivedSemanticKeys)) {
throw new IllegalArgumentException(
"host projection for hostId '" + hostProjection.hostId()
+ "' must publish the same semantic keys as the default theme");
}
for (final FrontendHostProjectionEntrySpec tokenProjection : hostProjection.tokenProjections()) {
if (!derivedSemanticKeys.contains(tokenProjection.semanticKey())) {
throw new IllegalArgumentException(
"host projection for hostId '" + hostProjection.hostId() + "' references unknown semantic key: "
+ tokenProjection.semanticKey());
}
}
}
}
}
public static FrontendSemanticPresentationSpec empty() {
return new FrontendSemanticPresentationSpec(List.of(), List.of());
return new FrontendSemanticPresentationSpec(List.of(), "", List.of());
}
public FrontendVisualThemeSpec defaultTheme() {
if (themes.isEmpty()) {
throw new IllegalStateException("empty semantic presentation has no default theme");
}
return themes.stream()
.filter(theme -> theme.themeId().equals(defaultThemeId))
.findFirst()
.orElseThrow(() -> new IllegalStateException("defaultThemeId must resolve to a declared theme"));
}
public boolean isEmpty() {
return themes.isEmpty() && defaultThemeId.isEmpty() && hostProjections.isEmpty();
}
public List<String> semanticKeys() {
if (themes.isEmpty()) {
return List.of();
}
return defaultTheme().tokenStyles().stream()
.map(FrontendTokenStyleSpec::semanticKey)
.toList();
}
public java.util.Optional<FrontendHostProjectionSpec> hostProjection(final String hostId) {
return hostProjections.stream()
.filter(hostProjection -> hostProjection.hostId().equals(requireText(hostId, "hostId")))
.findFirst();
}
public FrontendHostProjectionSpec requireHostProjection(final String hostId) {
return hostProjection(hostId)
.orElseThrow(() -> new IllegalArgumentException("missing host projection for hostId: " + hostId));
}
private static String requireText(
final String value,
final String field) {
final String candidate = Objects.requireNonNull(value, field).trim();
if (candidate.isEmpty()) {
throw new IllegalArgumentException(field + " must not be blank");
}
return candidate;
}
}

View File

@ -0,0 +1,29 @@
package p.studio.compiler.models;
import java.util.Objects;
public record FrontendSemanticToken(
int startOffset,
int endOffset,
String semanticKey) {
public FrontendSemanticToken {
if (startOffset < 0) {
throw new IllegalArgumentException("startOffset must be >= 0");
}
if (endOffset < startOffset) {
throw new IllegalArgumentException("endOffset must be >= startOffset");
}
semanticKey = requireText(semanticKey, "semanticKey");
}
private static String requireText(
final String value,
final String field) {
final String candidate = Objects.requireNonNull(value, field).trim();
if (candidate.isEmpty()) {
throw new IllegalArgumentException(field + " must not be blank");
}
return candidate;
}
}

View File

@ -0,0 +1,14 @@
package p.studio.compiler.models;
import java.util.List;
@FunctionalInterface
public interface FrontendSemanticTokenProvider {
FrontendSemanticTokenProvider NOOP = documentText -> List.of();
List<FrontendSemanticToken> tokenize(String documentText);
static FrontendSemanticTokenProvider noop() {
return NOOP;
}
}

View File

@ -17,6 +17,8 @@ public class FrontendSpec {
private final List<Stdlib> stdlibVersions = List.of();
@Builder.Default
private final FrontendSemanticPresentationSpec semanticPresentation = FrontendSemanticPresentationSpec.empty();
@Builder.Default
private final FrontendSemanticTokenProvider semanticTokenProvider = FrontendSemanticTokenProvider.noop();
public String toString() {
return String.format("FrontendSpec(language=%s)", languageId);

View File

@ -0,0 +1,26 @@
package p.studio.compiler.models;
import java.util.Objects;
public record FrontendTokenStyleSpec(
String semanticKey,
String foreground,
boolean italic,
boolean bold,
boolean underline) {
public FrontendTokenStyleSpec {
semanticKey = requireText(semanticKey, "semanticKey");
foreground = requireText(foreground, "foreground");
}
private static String requireText(
final String value,
final String field) {
final String candidate = Objects.requireNonNull(value, field).trim();
if (candidate.isEmpty()) {
throw new IllegalArgumentException(field + " must not be blank");
}
return candidate;
}
}

View File

@ -0,0 +1,47 @@
package p.studio.compiler.models;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
public record FrontendVisualThemeSpec(
String themeId,
String displayName,
FrontendEditorPaletteSpec editorPalette,
List<FrontendTokenStyleSpec> tokenStyles) {
public FrontendVisualThemeSpec {
themeId = requireText(themeId, "themeId");
displayName = requireText(displayName, "displayName");
editorPalette = Objects.requireNonNull(editorPalette, "editorPalette");
tokenStyles = List.copyOf(Objects.requireNonNull(tokenStyles, "tokenStyles"));
if (tokenStyles.isEmpty()) {
throw new IllegalArgumentException("tokenStyles must not be empty");
}
final Map<String, FrontendTokenStyleSpec> bySemanticKey = new LinkedHashMap<>();
for (final FrontendTokenStyleSpec tokenStyle : tokenStyles) {
final FrontendTokenStyleSpec previous = bySemanticKey.put(tokenStyle.semanticKey(), tokenStyle);
if (previous != null) {
throw new IllegalArgumentException("duplicate token style for semantic key: " + tokenStyle.semanticKey());
}
}
}
public FrontendTokenStyleSpec requireTokenStyle(final String semanticKey) {
return tokenStyles.stream()
.filter(tokenStyle -> tokenStyle.semanticKey().equals(semanticKey))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("missing token style for semantic key: " + semanticKey));
}
private static String requireText(
final String value,
final String field) {
final String candidate = Objects.requireNonNull(value, field).trim();
if (candidate.isEmpty()) {
throw new IllegalArgumentException(field + " must not be blank");
}
return candidate;
}
}

View File

@ -1,8 +1,11 @@
package p.studio.compiler.utilities;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@FunctionalInterface
@ -24,5 +27,38 @@ public interface SourceProviderFactory {
};
}
// more providers can be added here, e.g., in-memory, etc.
static SourceProviderFactory overlayUtf8(final Map<Path, String> overrides) {
final Map<Path, byte[]> bytesByPath = new HashMap<>();
for (final var entry : Objects.requireNonNull(overrides, "overrides").entrySet()) {
bytesByPath.put(
normalize(entry.getKey()),
Objects.requireNonNull(entry.getValue(), "override text").getBytes(StandardCharsets.UTF_8));
}
return overlay(bytesByPath, filesystem());
}
static SourceProviderFactory overlay(
final Map<Path, byte[]> overrides,
final SourceProviderFactory fallback) {
final Map<Path, byte[]> normalizedOverrides = new HashMap<>();
for (final var entry : Objects.requireNonNull(overrides, "overrides").entrySet()) {
normalizedOverrides.put(
normalize(entry.getKey()),
Objects.requireNonNull(entry.getValue(), "override bytes").clone());
}
final SourceProviderFactory safeFallback = Objects.requireNonNull(fallback, "fallback");
return path -> {
final Path normalizedPath = normalize(path);
final byte[] override = normalizedOverrides.get(normalizedPath);
if (override != null) {
final byte[] immutableBytes = override.clone();
return () -> immutableBytes;
}
return safeFallback.create(normalizedPath);
};
}
private static Path normalize(final Path path) {
return Objects.requireNonNull(path, "path").toAbsolutePath().normalize();
}
}

View File

@ -0,0 +1,100 @@
package p.studio.compiler.models;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
final class FrontendSemanticPresentationSpecTest {
@Test
void shouldExposeHostProjectionByHostId() {
final var presentation = new FrontendSemanticPresentationSpec(
List.of(new FrontendVisualThemeSpec(
"demo-theme",
"Demo Theme",
new FrontendEditorPaletteSpec("#111111", "#eeeeee", "#333333", "#777777", "#111111", "#222222", "#eeeeee"),
List.of(new FrontendTokenStyleSpec("demo-key", "#eeeeee", false, false, false)))),
"demo-theme",
List.of(new FrontendHostProjectionSpec(
"vscode",
List.of(new FrontendHostProjectionEntrySpec(
"demo-key",
"keyword",
List.of("declaration"),
"variable",
List.of())))));
final var vscodeProjection = presentation.requireHostProjection("vscode").requireProjection("demo-key");
assertEquals("keyword", vscodeProjection.hostTokenType());
assertEquals(List.of("declaration"), vscodeProjection.hostTokenModifiers());
assertEquals("variable", vscodeProjection.fallbackTokenType());
}
@Test
void shouldRejectDuplicateHostIds() {
assertThrows(IllegalArgumentException.class, () -> new FrontendSemanticPresentationSpec(
List.of(new FrontendVisualThemeSpec(
"demo-theme",
"Demo Theme",
new FrontendEditorPaletteSpec("#111111", "#eeeeee", "#333333", "#777777", "#111111", "#222222", "#eeeeee"),
List.of(new FrontendTokenStyleSpec("demo-key", "#eeeeee", false, false, false)))),
"demo-theme",
List.of(
new FrontendHostProjectionSpec(
"vscode",
List.of(new FrontendHostProjectionEntrySpec(
"demo-key",
"keyword",
List.of(),
"variable",
List.of()))),
new FrontendHostProjectionSpec(
"vscode",
List.of(new FrontendHostProjectionEntrySpec(
"demo-key",
"type",
List.of(),
"variable",
List.of()))))));
}
@Test
void shouldRejectProjectionForUnknownSemanticKey() {
assertThrows(IllegalArgumentException.class, () -> new FrontendSemanticPresentationSpec(
List.of(new FrontendVisualThemeSpec(
"demo-theme",
"Demo Theme",
new FrontendEditorPaletteSpec("#111111", "#eeeeee", "#333333", "#777777", "#111111", "#222222", "#eeeeee"),
List.of(new FrontendTokenStyleSpec("demo-key", "#eeeeee", false, false, false)))),
"demo-theme",
List.of(new FrontendHostProjectionSpec(
"vscode",
List.of(new FrontendHostProjectionEntrySpec(
"unknown-key",
"keyword",
List.of(),
"variable",
List.of()))))));
}
@Test
void shouldRejectThemeSetThatDriftsFromDefaultThemeVocabulary() {
assertThrows(IllegalArgumentException.class, () -> new FrontendSemanticPresentationSpec(
List.of(
new FrontendVisualThemeSpec(
"demo-theme",
"Demo Theme",
new FrontendEditorPaletteSpec("#111111", "#eeeeee", "#333333", "#777777", "#111111", "#222222", "#eeeeee"),
List.of(new FrontendTokenStyleSpec("demo-key", "#eeeeee", false, false, false))),
new FrontendVisualThemeSpec(
"demo-theme-2",
"Demo Theme 2",
new FrontendEditorPaletteSpec("#111111", "#eeeeee", "#333333", "#777777", "#111111", "#222222", "#eeeeee"),
List.of(new FrontendTokenStyleSpec("other-key", "#eeeeee", false, false, false)))),
"demo-theme",
List.of()));
}
}

View File

@ -3,5 +3,4 @@ plugins {
}
dependencies {
implementation(project(":prometeu-vfs:prometeu-vfs-api"))
}

View File

@ -1,21 +0,0 @@
package p.studio.lsp;
import p.studio.lsp.dtos.LspSessionStateDTO;
import p.studio.lsp.messages.*;
import p.studio.vfs.VfsProjectDocument;
public interface LspService extends AutoCloseable {
LspProjectContext projectContext();
VfsProjectDocument projectDocumentVfs();
LspSessionStateDTO snapshot();
LspAnalyzeDocumentResult analyzeDocument(LspAnalyzeDocumentRequest request);
LspDefinitionResult definition(LspDefinitionRequest request);
@Override
default void close() {
}
}

View File

@ -1,8 +0,0 @@
package p.studio.lsp;
import p.studio.lsp.messages.LspProjectContext;
import p.studio.vfs.VfsProjectDocument;
public interface LspServiceFactory {
LspService open(LspProjectContext projectContext, VfsProjectDocument vfsProjectDocument);
}

View File

@ -0,0 +1,10 @@
package p.studio.lsp.api;
import p.studio.lsp.messages.LspProjectContext;
import p.studio.lsp.messages.LspServerConfiguration;
public interface LspServerLifecycle {
void bootServer(LspServerConfiguration serverConfiguration, LspProjectContext context);
void shutdownServer(LspProjectContext context);
}

View File

@ -1,16 +0,0 @@
package p.studio.lsp.dtos;
import java.nio.file.Path;
import java.util.Objects;
public record LspDefinitionTargetDTO(
String name,
Path documentPath,
LspRangeDTO range) {
public LspDefinitionTargetDTO {
name = Objects.requireNonNull(name, "name");
documentPath = Objects.requireNonNull(documentPath, "documentPath").toAbsolutePath().normalize();
range = Objects.requireNonNull(range, "range");
}
}

View File

@ -1,24 +0,0 @@
package p.studio.lsp.dtos;
import p.studio.lsp.messages.LspDiagnosticSeverity;
import java.nio.file.Path;
import java.util.Objects;
public record LspDiagnosticDTO(
Path documentPath,
LspRangeDTO range,
LspDiagnosticSeverity severity,
String phase,
String code,
String message) {
public LspDiagnosticDTO {
documentPath = Objects.requireNonNull(documentPath, "documentPath").toAbsolutePath().normalize();
range = Objects.requireNonNull(range, "range");
severity = Objects.requireNonNull(severity, "severity");
phase = phase == null ? "" : phase;
code = code == null ? "" : code;
message = Objects.requireNonNull(message, "message");
}
}

View File

@ -1,13 +0,0 @@
package p.studio.lsp.dtos;
import java.util.Objects;
public record LspHighlightSpanDTO(
LspRangeDTO range,
String semanticKey) {
public LspHighlightSpanDTO {
range = Objects.requireNonNull(range, "range");
semanticKey = Objects.requireNonNull(semanticKey, "semanticKey");
}
}

View File

@ -1,18 +0,0 @@
package p.studio.lsp.dtos;
import java.util.Objects;
public record LspInlineHintDTO(
LspRangeDTO anchor,
String label,
String category) {
public LspInlineHintDTO {
anchor = Objects.requireNonNull(anchor, "anchor");
label = Objects.requireNonNull(label, "label").trim();
category = Objects.requireNonNull(category, "category").trim();
if (label.isBlank()) {
throw new IllegalArgumentException("label cannot be blank");
}
}
}

View File

@ -1,15 +0,0 @@
package p.studio.lsp.dtos;
public record LspRangeDTO(
int startOffset,
int endOffset) {
public LspRangeDTO {
startOffset = Math.max(0, startOffset);
endOffset = Math.max(startOffset, endOffset);
}
public boolean contains(final int offset) {
return startOffset <= offset && offset < endOffset;
}
}

View File

@ -1,14 +0,0 @@
package p.studio.lsp.dtos;
import java.util.List;
import java.util.Objects;
public record LspSemanticPresentationDTO(
List<String> semanticKeys,
List<String> resources) {
public LspSemanticPresentationDTO {
semanticKeys = List.copyOf(Objects.requireNonNull(semanticKeys, "semanticKeys"));
resources = List.copyOf(Objects.requireNonNull(resources, "resources"));
}
}

View File

@ -1,13 +0,0 @@
package p.studio.lsp.dtos;
import java.util.List;
import java.util.Objects;
public record LspSessionStateDTO(
boolean semanticReadReady,
List<String> declaredCapabilities) {
public LspSessionStateDTO {
declaredCapabilities = List.copyOf(Objects.requireNonNull(declaredCapabilities, "declaredCapabilities"));
}
}

View File

@ -1,18 +0,0 @@
package p.studio.lsp.dtos;
import java.util.List;
import java.util.Objects;
public record LspStructuralAnchorDTO(
LspRangeDTO range,
LspRangeDTO startAnchor,
LspRangeDTO endAnchor,
List<LspStructuralAnchorDTO> children) {
public LspStructuralAnchorDTO {
range = Objects.requireNonNull(range, "range");
startAnchor = Objects.requireNonNull(startAnchor, "startAnchor");
endAnchor = Objects.requireNonNull(endAnchor, "endAnchor");
children = List.copyOf(Objects.requireNonNull(children, "children"));
}
}

View File

@ -1,23 +0,0 @@
package p.studio.lsp.dtos;
import p.studio.lsp.messages.LspSymbolKind;
import java.nio.file.Path;
import java.util.List;
import java.util.Objects;
public record LspSymbolDTO(
String name,
LspSymbolKind kind,
Path documentPath,
LspRangeDTO range,
List<LspSymbolDTO> children) {
public LspSymbolDTO {
name = Objects.requireNonNull(name, "name");
kind = Objects.requireNonNull(kind, "kind");
documentPath = Objects.requireNonNull(documentPath, "documentPath").toAbsolutePath().normalize();
range = Objects.requireNonNull(range, "range");
children = List.copyOf(Objects.requireNonNull(children, "children"));
}
}

View File

@ -1,13 +0,0 @@
package p.studio.lsp.events;
import java.util.Objects;
public record LspEvent(
LspEventKind kind,
String message) {
public LspEvent {
Objects.requireNonNull(kind, "kind");
Objects.requireNonNull(message, "message");
}
}

View File

@ -1,5 +0,0 @@
package p.studio.lsp.events;
public enum LspEventKind {
SESSION_READY
}

View File

@ -1,5 +0,0 @@
package p.studio.lsp.events;
public interface LspEventSink {
void publish(LspEvent event);
}

View File

@ -1,12 +0,0 @@
package p.studio.lsp.messages;
import java.nio.file.Path;
import java.util.Objects;
public record LspAnalyzeDocumentRequest(
Path documentPath) {
public LspAnalyzeDocumentRequest {
documentPath = Objects.requireNonNull(documentPath, "documentPath").toAbsolutePath().normalize();
}
}

View File

@ -1,34 +0,0 @@
package p.studio.lsp.messages;
import p.studio.lsp.dtos.LspDiagnosticDTO;
import p.studio.lsp.dtos.LspHighlightSpanDTO;
import p.studio.lsp.dtos.LspInlineHintDTO;
import p.studio.lsp.dtos.LspSemanticPresentationDTO;
import p.studio.lsp.dtos.LspSessionStateDTO;
import p.studio.lsp.dtos.LspStructuralAnchorDTO;
import p.studio.lsp.dtos.LspSymbolDTO;
import java.util.List;
import java.util.Objects;
public record LspAnalyzeDocumentResult(
LspSessionStateDTO sessionState,
LspSemanticPresentationDTO semanticPresentation,
List<LspDiagnosticDTO> diagnostics,
List<LspHighlightSpanDTO> semanticHighlights,
List<LspInlineHintDTO> inlineHints,
List<LspSymbolDTO> documentSymbols,
List<LspStructuralAnchorDTO> structuralAnchors,
List<LspSymbolDTO> workspaceSymbols) {
public LspAnalyzeDocumentResult {
Objects.requireNonNull(sessionState, "sessionState");
Objects.requireNonNull(semanticPresentation, "semanticPresentation");
diagnostics = List.copyOf(Objects.requireNonNull(diagnostics, "diagnostics"));
semanticHighlights = List.copyOf(Objects.requireNonNull(semanticHighlights, "semanticHighlights"));
inlineHints = List.copyOf(Objects.requireNonNull(inlineHints, "inlineHints"));
documentSymbols = List.copyOf(Objects.requireNonNull(documentSymbols, "documentSymbols"));
structuralAnchors = List.copyOf(Objects.requireNonNull(structuralAnchors, "structuralAnchors"));
workspaceSymbols = List.copyOf(Objects.requireNonNull(workspaceSymbols, "workspaceSymbols"));
}
}

View File

@ -1,14 +0,0 @@
package p.studio.lsp.messages;
import java.nio.file.Path;
import java.util.Objects;
public record LspDefinitionRequest(
Path documentPath,
int offset) {
public LspDefinitionRequest {
documentPath = Objects.requireNonNull(documentPath, "documentPath").toAbsolutePath().normalize();
offset = Math.max(0, offset);
}
}

View File

@ -1,19 +0,0 @@
package p.studio.lsp.messages;
import p.studio.lsp.dtos.LspDefinitionTargetDTO;
import java.nio.file.Path;
import java.util.List;
import java.util.Objects;
public record LspDefinitionResult(
Path documentPath,
int offset,
List<LspDefinitionTargetDTO> targets) {
public LspDefinitionResult {
documentPath = Objects.requireNonNull(documentPath, "documentPath").toAbsolutePath().normalize();
offset = Math.max(0, offset);
targets = List.copyOf(Objects.requireNonNull(targets, "targets"));
}
}

View File

@ -1,6 +0,0 @@
package p.studio.lsp.messages;
public enum LspDiagnosticSeverity {
ERROR,
WARNING
}

View File

@ -4,13 +4,23 @@ import java.nio.file.Path;
import java.util.Objects;
public record LspProjectContext(
String projectName,
String projectKey,
String languageId,
Path rootPath) {
Path projectRoot) {
public LspProjectContext {
Objects.requireNonNull(projectName, "projectName");
Objects.requireNonNull(languageId, "languageId");
rootPath = Objects.requireNonNull(rootPath, "rootPath").toAbsolutePath().normalize();
projectKey = requireText(projectKey, "projectKey");
languageId = requireText(languageId, "languageId");
projectRoot = Objects.requireNonNull(projectRoot, "projectRoot").toAbsolutePath().normalize();
}
private static String requireText(
final String value,
final String field) {
final String candidate = Objects.requireNonNull(value, field).trim();
if (candidate.isEmpty()) {
throw new IllegalArgumentException(field + " must not be blank");
}
return candidate;
}
}

View File

@ -0,0 +1,19 @@
package p.studio.lsp.messages;
import lombok.Builder;
import lombok.Getter;
@Builder
@Getter
public class LspServerConfiguration {
public static final String DEFAULT_HOST = "127.0.0.1";
public static final int DEFAULT_PORT = 7775;
@Builder.Default
private final LspServerEndpoint endpoint = new LspServerEndpoint(DEFAULT_HOST, DEFAULT_PORT);
public static LspServerConfiguration defaults() {
return LspServerConfiguration.builder().build();
}
}

View File

@ -0,0 +1,19 @@
package p.studio.lsp.messages;
import java.util.Objects;
public record LspServerEndpoint(
String host,
int port) {
public LspServerEndpoint {
final String candidate = Objects.requireNonNull(host, "host").trim();
if (candidate.isEmpty()) {
throw new IllegalArgumentException("host must not be blank");
}
if (port < 0 || port > 65535) {
throw new IllegalArgumentException("port must be between 0 and 65535");
}
host = candidate;
}
}

View File

@ -1,22 +0,0 @@
package p.studio.lsp.dtos;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
final class LspInlineHintDTOTest {
@Test
void trimsLabelAndCategory() {
final var hint = new LspInlineHintDTO(new LspRangeDTO(3, 3), " int ", " type ");
assertEquals("int", hint.label());
assertEquals("type", hint.category());
}
@Test
void rejectsBlankLabels() {
assertThrows(IllegalArgumentException.class, () ->
new LspInlineHintDTO(new LspRangeDTO(1, 1), " ", "type"));
}
}

View File

@ -4,13 +4,11 @@ plugins {
dependencies {
implementation(project(":prometeu-lsp:prometeu-lsp-api"))
implementation(project(":prometeu-vfs:prometeu-vfs-api"))
implementation(project(":prometeu-compiler:prometeu-compiler-core"))
implementation(project(":prometeu-compiler:prometeu-frontend-api"))
implementation(project(":prometeu-compiler:prometeu-frontend-registry"))
implementation(project(":prometeu-compiler:prometeu-deps"))
implementation(project(":prometeu-compiler:prometeu-build-pipeline"))
implementation(project(":prometeu-compiler:frontends:prometeu-frontend-pbs"))
testImplementation(project(":prometeu-vfs:prometeu-vfs-v1"))
implementation(project(":prometeu-compiler:prometeu-frontend-registry"))
implementation(project(":prometeu-compiler:prometeu-deps"))
implementation(libs.lsp4j)
}

View File

@ -1,27 +0,0 @@
package p.studio.lsp;
import p.studio.lsp.dtos.LspSessionStateDTO;
import p.studio.lsp.messages.LspAnalyzeDocumentRequest;
import p.studio.lsp.messages.LspAnalyzeDocumentResult;
import p.studio.lsp.messages.SemanticSession;
import java.util.List;
import static p.studio.lsp.LspSemanticUtilities.normalize;
class LspSemanticAnalyseService {
LspAnalyzeDocumentResult analyze(
final SemanticSession session,
final LspAnalyzeDocumentRequest request) {
final var normalizedRequestedDocument = normalize(request.documentPath());
return new LspAnalyzeDocumentResult(
new LspSessionStateDTO(true, List.of("diagnostics", "symbols", "definition", "highlight", "structural-anchors")),
session.semanticPresentation(),
session.diagnosticsByDocument().getOrDefault(normalizedRequestedDocument, List.of()),
session.semanticHighlightsByDocument().getOrDefault(normalizedRequestedDocument, List.of()),
session.inlineHintsByDocument().getOrDefault(normalizedRequestedDocument, List.of()),
session.documentSymbolsByDocument().getOrDefault(normalizedRequestedDocument, List.of()),
session.structuralAnchorsByDocument().getOrDefault(normalizedRequestedDocument, List.of()),
session.workspaceSymbols());
}
}

View File

@ -1,46 +0,0 @@
package p.studio.lsp;
import p.studio.compiler.pbs.lexer.PbsToken;
import p.studio.compiler.pbs.lexer.PbsTokenKind;
import p.studio.lsp.dtos.LspDefinitionTargetDTO;
import p.studio.lsp.dtos.LspSymbolDTO;
import p.studio.lsp.messages.LspDefinitionRequest;
import p.studio.lsp.messages.LspDefinitionResult;
import p.studio.lsp.messages.SemanticSession;
import java.nio.file.Path;
import java.util.List;
import static p.studio.lsp.LspSemanticUtilities.normalize;
import static p.studio.lsp.LspSemanticUtilities.tokenAt;
class LspSemanticDefinitionService {
LspDefinitionResult definition(
final SemanticSession session,
final LspDefinitionRequest request) {
final List<LspDefinitionTargetDTO> targets = resolveDefinitionTargets(session, request);
return new LspDefinitionResult(request.documentPath(), request.offset(), targets);
}
private List<LspDefinitionTargetDTO> resolveDefinitionTargets(
final SemanticSession session,
final LspDefinitionRequest request) {
final Path documentPath = normalize(request.documentPath());
final List<PbsToken> tokens = session.tokensByDocument().getOrDefault(documentPath, List.of());
final PbsToken activeToken = tokenAt(tokens, request.offset());
if (activeToken == null || activeToken.kind() != PbsTokenKind.IDENTIFIER) {
return List.of();
}
final String lexeme = activeToken.lexeme();
final List<LspSymbolDTO> sameDocumentMatches = session.symbolsByName().getOrDefault(lexeme, List.of()).stream()
.filter(symbol -> symbol.documentPath().equals(documentPath))
.toList();
final List<LspSymbolDTO> effectiveMatches = sameDocumentMatches.isEmpty()
? session.symbolsByName().getOrDefault(lexeme, List.of())
: sameDocumentMatches;
return effectiveMatches.stream()
.map(symbol -> new LspDefinitionTargetDTO(symbol.name(), symbol.documentPath(), symbol.range()))
.toList();
}
}

View File

@ -1,353 +0,0 @@
package p.studio.lsp;
import p.studio.compiler.FrontendRegistryService;
import p.studio.compiler.messages.*;
import p.studio.compiler.models.AnalysisSnapshot;
import p.studio.compiler.models.BuilderPipelineContext;
import p.studio.compiler.models.FrontendSpec;
import p.studio.compiler.models.FrontendSemanticPresentationSpec;
import p.studio.compiler.models.SourceHandle;
import p.studio.compiler.pbs.ast.PbsAst;
import p.studio.compiler.pbs.lexer.PbsLexer;
import p.studio.compiler.pbs.parser.PbsParser;
import p.studio.compiler.source.diagnostics.Diagnostic;
import p.studio.compiler.source.diagnostics.DiagnosticSink;
import p.studio.compiler.source.identifiers.FileId;
import p.studio.compiler.services.PBSFrontendPhaseService;
import p.studio.compiler.services.PbsInlineHintSurface;
import p.studio.compiler.workspaces.AssetSurfaceContextLoader;
import p.studio.compiler.workspaces.PipelineStage;
import p.studio.compiler.workspaces.stages.LoadSourcesPipelineStage;
import p.studio.compiler.workspaces.stages.ResolveDepsPipelineStage;
import p.studio.lsp.dtos.LspDiagnosticDTO;
import p.studio.lsp.dtos.LspInlineHintDTO;
import p.studio.lsp.dtos.LspRangeDTO;
import p.studio.lsp.dtos.LspSemanticPresentationDTO;
import p.studio.lsp.messages.*;
import p.studio.lsp.models.AnalysisRuntimeSnapshot;
import p.studio.lsp.models.SemanticIndex;
import p.studio.utilities.logs.LogAggregator;
import p.studio.vfs.VfsProjectDocument;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static p.studio.lsp.LspSemanticUtilities.*;
final class LspSemanticReadPhase {
private final LspSemanticAnalyseService analyseService;
private final LspSemanticDefinitionService definitionService;
LspSemanticReadPhase() {
this.analyseService = new LspSemanticAnalyseService();
this.definitionService = new LspSemanticDefinitionService();
}
public LspAnalyzeDocumentResult analyze(
final LspProjectContext projectContext,
final VfsProjectDocument vfsProjectDocument,
final LspAnalyzeDocumentRequest request) {
final var session = buildSession(projectContext, vfsProjectDocument, request.documentPath());
return analyseService.analyze(session, request);
}
public LspDefinitionResult definition(
final LspProjectContext projectContext,
final VfsProjectDocument vfsProjectDocument,
final LspDefinitionRequest request) {
final var session = buildSession(projectContext, vfsProjectDocument, request.documentPath());
return definitionService.definition(session, request);
}
public static SemanticSession buildSession(
final LspProjectContext projectContext,
final VfsProjectDocument vfsProjectDocument,
final Path requestedDocumentPath) {
final BuilderPipelineConfig config = new BuilderPipelineConfig(
false,
projectContext.rootPath().toString(),
"core-v1",
new OverlaySourceProviderFactoryImpl(vfsProjectDocument, requestedDocumentPath));
final BuilderPipelineContext context = BuilderPipelineContext.fromConfig(config);
final AnalysisRuntimeSnapshot snapshot = runAnalysisStages(context);
return index(snapshot, context, requestedDocumentPath);
}
private static AnalysisRuntimeSnapshot runAnalysisStages(final BuilderPipelineContext context) {
final LogAggregator logs = LogAggregator.empty();
final List<BuildingIssue> diagnostics = new ArrayList<>();
final List<PipelineStage> stages = List.of(
new ResolveDepsPipelineStage(),
new LoadSourcesPipelineStage());
for (final PipelineStage stage : stages) {
final BuildingIssueSink stageIssues = stage.run(context, logs);
diagnostics.addAll(stageIssues.asCollection());
if (stageIssues.hasErrors()) {
break;
}
}
final DiagnosticSink frontendDiagnostics = DiagnosticSink.empty();
FrontendSpec frontendSpec = FrontendRegistryService.getDefaultFrontendSpec();
if (context.resolvedWorkspace != null && context.fileTable != null) {
final BuildingIssueSink frontendIssues = runFrontendPhase(context, logs, frontendDiagnostics);
diagnostics.addAll(frontendIssues.asCollection());
frontendSpec = context.resolvedWorkspace.frontendSpec();
}
return new AnalysisRuntimeSnapshot(
new AnalysisSnapshot(
frontendSpec,
diagnostics,
context.resolvedWorkspace,
context.fileTable,
context.irBackend),
List.copyOf(frontendDiagnostics.asCollection()));
}
private static BuildingIssueSink runFrontendPhase(
final BuilderPipelineContext context,
final LogAggregator logs,
final DiagnosticSink frontendDiagnostics) {
final var frontendSpec = context.resolvedWorkspace.frontendSpec();
final var service = FrontendRegistryService.getFrontendPhaseService(frontendSpec.getLanguageId());
if (service.isEmpty()) {
return BuildingIssueSink.empty().report(builder -> builder
.error(true)
.message("[BUILD]: unable to find a service for frontend phase: " + frontendSpec.getLanguageId()));
}
final FESurfaceContext feSurfaceContext = new AssetSurfaceContextLoader().load(context.resolvedWorkspace.mainProject().getRootPath());
final FrontendPhaseContext frontendPhaseContext = new FrontendPhaseContext(
context.resolvedWorkspace.graph().projectTable(),
context.fileTable,
context.resolvedWorkspace.stack(),
context.resolvedWorkspace.stdlib(),
HostAdmissionContext.permissiveDefault(),
feSurfaceContext);
final BuildingIssueSink issues = BuildingIssueSink.empty();
context.irBackend = service.get().compile(frontendPhaseContext, frontendDiagnostics, logs, issues);
return issues;
}
private static SemanticSession index(
final AnalysisRuntimeSnapshot runtimeSnapshot,
final BuilderPipelineContext context,
final Path requestedDocumentPath) {
final AnalysisSnapshot snapshot = runtimeSnapshot.analysisSnapshot();
final Map<Path, List<LspDiagnosticDTO>> diagnosticsByDocument = diagnosticsByDocument(
snapshot.diagnostics(),
snapshot,
runtimeSnapshot.frontendDiagnostics());
final SemanticIndex semanticIndex = new SemanticIndex();
if (snapshot.fileTable() == null) {
return new SemanticSession(
normalize(requestedDocumentPath),
semanticPresentation(snapshot.frontendSpec()),
diagnosticsByDocument,
Map.of(),
Map.of(),
Map.of(),
Map.of(),
List.of(),
Map.of(),
Map.of());
}
final Map<FileId, List<PbsAst.TopDecl>> importedSupplementalTopDeclsByFile = importedSupplementalTopDeclsByFile(
snapshot.frontendSpec(),
context);
final Map<Path, List<LspInlineHintDTO>> inlineHintsByDocument = inlineHintsByDocument(
snapshot,
snapshot.frontendSpec(),
context);
final List<IndexedDocument> indexedDocuments = new ArrayList<>();
for (final FileId fileId : snapshot.fileTable()) {
final SourceHandle sourceHandle = snapshot.fileTable().get(fileId);
if (!isSourceRelated(snapshot.frontendSpec(), sourceHandle)) {
continue;
}
final String source = sourceHandle.readUtf8().orElse("");
final DiagnosticSink diagnostics = DiagnosticSink.empty();
final var tokens = PbsLexer.lex(source, fileId, diagnostics);
final PbsAst.File ast = PbsParser.parse(tokens, fileId, diagnostics, PbsParser.ParseMode.ORDINARY);
final Path documentPath = sourceHandle.getCanonPath();
semanticIndex.registerDocument(documentPath, ast, tokens.asList());
indexedDocuments.add(new IndexedDocument(
fileId,
documentPath,
importedSupplementalTopDeclsByFile.getOrDefault(fileId, List.of())));
}
for (final IndexedDocument indexedDocument : indexedDocuments) {
semanticIndex.buildHighlights(
indexedDocument.documentPath(),
indexedDocument.visibleImportedTopDecls());
}
return new SemanticSession(
normalize(requestedDocumentPath),
semanticPresentation(snapshot.frontendSpec()),
diagnosticsByDocument,
semanticIndex.semanticHighlightsByDocument(),
inlineHintsByDocument,
semanticIndex.documentSymbolsByDocument(),
semanticIndex.structuralAnchorsByDocument(),
semanticIndex.workspaceSymbols(),
semanticIndex.symbolsByName(),
semanticIndex.tokensByDocument());
}
private static Map<FileId, List<PbsAst.TopDecl>> importedSupplementalTopDeclsByFile(
final FrontendSpec frontendSpec,
final BuilderPipelineContext context) {
final var surface = semanticReadSurface(frontendSpec, context);
final Map<FileId, List<PbsAst.TopDecl>> byFile = new LinkedHashMap<>();
for (final var entry : surface.supplementalTopDeclsByFile().entrySet()) {
byFile.put(entry.getKey(), entry.getValue().asList());
}
return Map.copyOf(byFile);
}
private static Map<Path, List<LspInlineHintDTO>> inlineHintsByDocument(
final AnalysisSnapshot snapshot,
final FrontendSpec frontendSpec,
final BuilderPipelineContext context) {
if (snapshot.fileTable() == null) {
return Map.of();
}
final var surface = semanticReadSurface(frontendSpec, context);
final Map<Path, List<LspInlineHintDTO>> byDocument = new LinkedHashMap<>();
for (final var entry : surface.inlineHintsByFile().entrySet()) {
final SourceHandle sourceHandle = snapshot.fileTable().get(entry.getKey());
final Path documentPath = sourceHandle.getCanonPath().toAbsolutePath().normalize();
final var hints = entry.getValue().asList().stream()
.map(LspSemanticReadPhase::toInlineHintDTO)
.toList();
if (!hints.isEmpty()) {
byDocument.put(documentPath, hints);
}
}
return Map.copyOf(byDocument);
}
private static PBSFrontendPhaseService.PbsSemanticReadSurface semanticReadSurface(
final FrontendSpec frontendSpec,
final BuilderPipelineContext context) {
if (context.resolvedWorkspace == null || context.fileTable == null || !"pbs".equals(frontendSpec.getLanguageId())) {
return new PBSFrontendPhaseService.PbsSemanticReadSurface(Map.of(), Map.of());
}
final FESurfaceContext feSurfaceContext = new AssetSurfaceContextLoader().load(context.resolvedWorkspace.mainProject().getRootPath());
final FrontendPhaseContext frontendPhaseContext = new FrontendPhaseContext(
context.resolvedWorkspace.graph().projectTable(),
context.fileTable,
context.resolvedWorkspace.stack(),
context.resolvedWorkspace.stdlib(),
HostAdmissionContext.permissiveDefault(),
feSurfaceContext);
final var diagnostics = DiagnosticSink.empty();
final var issues = BuildingIssueSink.empty();
return PBSFrontendPhaseService.semanticReadSurface(
frontendPhaseContext,
diagnostics,
issues);
}
private static LspInlineHintDTO toInlineHintDTO(final PbsInlineHintSurface hint) {
return new LspInlineHintDTO(
new LspRangeDTO(
(int) hint.anchorSpan().getStart(),
(int) hint.anchorSpan().getEnd()),
hint.label(),
hint.category());
}
private record IndexedDocument(
FileId fileId,
Path documentPath,
List<PbsAst.TopDecl> visibleImportedTopDecls) {
}
private static boolean isSourceRelated(final FrontendSpec frontendSpec, final SourceHandle sourceHandle) {
return frontendSpec.getAllowedExtensions().contains(sourceHandle.getExtension());
}
private static LspSemanticPresentationDTO semanticPresentation(final FrontendSpec frontendSpec) {
final FrontendSemanticPresentationSpec presentation = frontendSpec.getSemanticPresentation();
return new LspSemanticPresentationDTO(presentation.semanticKeys(), presentation.resources());
}
private static Map<Path, List<LspDiagnosticDTO>> diagnosticsByDocument(
final List<BuildingIssue> issues,
final AnalysisSnapshot snapshot,
final List<Diagnostic> frontendDiagnostics) {
final Map<Path, List<LspDiagnosticDTO>> diagnosticsByDocument = new LinkedHashMap<>();
diagnosticIssues(issues, snapshot, diagnosticsByDocument);
diagnosticFrontend(frontendDiagnostics, snapshot, diagnosticsByDocument);
return freezeMapOfLists(diagnosticsByDocument);
}
private static boolean isValidIssueDiagnostic(
final AnalysisSnapshot snapshot,
final BuildingIssue issue) {
if (snapshot.fileTable() == null || issue.getFileId() == null || issue.getFileId() < 0) {
return false;
}
return issue.getFileId() < snapshot.fileTable().size();
}
private static void diagnosticIssues(
final List<BuildingIssue> issues,
final AnalysisSnapshot snapshot,
final Map<Path, List<LspDiagnosticDTO>> diagnosticsByDocument) {
for (final var issue : issues) {
if (!isValidIssueDiagnostic(snapshot, issue)) {
continue;
}
final SourceHandle sourceHandle = snapshot.fileTable().get(new FileId(issue.getFileId()));
final Path documentPath = sourceHandle.getCanonPath().toAbsolutePath().normalize();
diagnosticsByDocument
.computeIfAbsent(documentPath, ignored -> new ArrayList<>())
.add(new LspDiagnosticDTO(
documentPath,
new LspRangeDTO(
safeOffset(issue.getStart()),
safeEnd(issue.getStart(), issue.getEnd())),
issue.isError()
? LspDiagnosticSeverity.ERROR
: LspDiagnosticSeverity.WARNING,
issue.getPhase(),
issue.getCode(),
issue.getMessage()));
}
}
private static boolean isValidFEDiagnostic(
final AnalysisSnapshot snapshot,
final Diagnostic diagnostic) {
return snapshot.fileTable() != null && !diagnostic.getSpan().getFileId().isNone();
}
private static void diagnosticFrontend(
final List<Diagnostic> feDiagnostics,
final AnalysisSnapshot snapshot,
final Map<Path, List<LspDiagnosticDTO>> diagnosticsByDocument) {
for (final var feDiagnostic : feDiagnostics) {
if (!isValidFEDiagnostic(snapshot, feDiagnostic)) {
continue;
}
final SourceHandle sourceHandle = snapshot.fileTable().get(feDiagnostic.getSpan().getFileId());
final Path documentPath = sourceHandle.getCanonPath().toAbsolutePath().normalize();
diagnosticsByDocument
.computeIfAbsent(documentPath, ignored -> new ArrayList<>())
.add(new LspDiagnosticDTO(
documentPath,
new LspRangeDTO(
(int) feDiagnostic.getSpan().getStart(),
(int) feDiagnostic.getSpan().getEnd()),
feDiagnostic.getSeverity().isError()
? LspDiagnosticSeverity.ERROR
: LspDiagnosticSeverity.WARNING,
feDiagnostic.getPhase().name(),
feDiagnostic.getCode(),
feDiagnostic.getMessage()));
}
}
}

View File

@ -1,52 +0,0 @@
package p.studio.lsp;
import lombok.experimental.UtilityClass;
import p.studio.compiler.pbs.lexer.PbsToken;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@UtilityClass
public class LspSemanticUtilities {
public static PbsToken tokenAt(final List<PbsToken> tokens, final int offset) {
for (final PbsToken token : tokens) {
if (token.start() <= offset && offset < token.end()) {
return token;
}
}
return null;
}
public static Path normalize(final Path path) {
final Path normalized = Objects.requireNonNull(path, "path").toAbsolutePath().normalize();
try {
return Files.exists(normalized) ? normalized.toRealPath() : normalized;
} catch (IOException ignored) {
return normalized;
}
}
public static <T> Map<Path, List<T>> freezeMapOfLists(final Map<Path, List<T>> mutable) {
final Map<Path, List<T>> frozen = new LinkedHashMap<>();
for (final var entry : mutable.entrySet()) {
frozen.put(entry.getKey(), List.copyOf(entry.getValue()));
}
return Map.copyOf(frozen);
}
public static int safeOffset(final Integer value) {
return value == null ? 0 : Math.max(0, value);
}
public static int safeEnd(final Integer start, final Integer end) {
final int safeStart = safeOffset(start);
final int safeEnd = safeOffset(end);
return Math.max(safeStart, safeEnd);
}
}

View File

@ -1,17 +0,0 @@
package p.studio.lsp;
import p.studio.lsp.messages.LspProjectContext;
import p.studio.vfs.VfsProjectDocument;
import java.util.Objects;
public final class LspServiceFactoryImpl implements LspServiceFactory {
@Override
public LspService open(
final LspProjectContext projectContext,
final VfsProjectDocument vfsProjectDocument) {
return new LspServiceImpl(
Objects.requireNonNull(projectContext, "projectContext"),
Objects.requireNonNull(vfsProjectDocument, "vfsProjectDocument"));
}
}

View File

@ -1,50 +0,0 @@
package p.studio.lsp;
import p.studio.lsp.dtos.LspSessionStateDTO;
import p.studio.lsp.messages.*;
import p.studio.vfs.VfsProjectDocument;
import java.util.List;
import java.util.Objects;
public final class LspServiceImpl implements LspService {
private final LspProjectContext projectContext;
private final VfsProjectDocument vfsProjectDocument;
private final LspSemanticReadPhase semanticReadPhase;
public LspServiceImpl(
final LspProjectContext projectContext,
final VfsProjectDocument vfsProjectDocument) {
this.projectContext = Objects.requireNonNull(projectContext, "projectContext");
this.vfsProjectDocument = Objects.requireNonNull(vfsProjectDocument, "vfsProjectDocument");
this.semanticReadPhase = new LspSemanticReadPhase();
}
@Override
public LspProjectContext projectContext() {
return projectContext;
}
@Override
public VfsProjectDocument projectDocumentVfs() {
return vfsProjectDocument;
}
@Override
public LspSessionStateDTO snapshot() {
return new LspSessionStateDTO(
true,
List.of("diagnostics", "symbols", "definition", "highlight"));
}
@Override
public LspAnalyzeDocumentResult analyzeDocument(final LspAnalyzeDocumentRequest request) {
return semanticReadPhase.analyze(projectContext, vfsProjectDocument, request);
}
@Override
public LspDefinitionResult definition(final LspDefinitionRequest request) {
return semanticReadPhase.definition(projectContext, vfsProjectDocument, request);
}
}

View File

@ -1,51 +0,0 @@
package p.studio.lsp;
import p.studio.compiler.utilities.SourceProvider;
import p.studio.compiler.utilities.SourceProviderFactory;
import p.studio.vfs.VfsDocumentOpenResult;
import p.studio.vfs.VfsProjectDocument;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
final class OverlaySourceProviderFactoryImpl implements SourceProviderFactory {
private final VfsProjectDocument vfsProjectDocument;
private final Path openedDocumentPath;
OverlaySourceProviderFactoryImpl(
final VfsProjectDocument vfsProjectDocument,
final Path openedDocumentPath) {
this.vfsProjectDocument = Objects.requireNonNull(vfsProjectDocument, "vfsProjectDocument");
this.openedDocumentPath = openedDocumentPath == null
? null
: normalize(openedDocumentPath);
}
@Override
public SourceProvider create(final Path path) {
final Path normalizedPath = normalize(path);
return () -> read(normalizedPath);
}
private byte[] read(final Path path) throws IOException {
if (openedDocumentPath != null && openedDocumentPath.equals(path)) {
final VfsDocumentOpenResult result = vfsProjectDocument.openDocument(path);
if (result instanceof VfsDocumentOpenResult.VfsTextDocument textDocument) {
return textDocument.content().getBytes(StandardCharsets.UTF_8);
}
}
return Files.readAllBytes(path);
}
private Path normalize(final Path path) {
final Path normalized = path.toAbsolutePath().normalize();
try {
return Files.exists(normalized) ? normalized.toRealPath() : normalized;
} catch (IOException ignored) {
return normalized;
}
}
}

View File

@ -0,0 +1,12 @@
package p.studio.lsp.messages;
import java.util.List;
import java.util.Objects;
public record BaselineCompletion(
boolean incomplete,
List<BaselineCompletionItem> items) {
public BaselineCompletion {
items = List.copyOf(Objects.requireNonNull(items, "items"));
}
}

View File

@ -0,0 +1,30 @@
package p.studio.lsp.messages;
import java.util.Objects;
public record BaselineCompletionItem(
String label,
BaselineCompletionItemKind kind,
String detail,
String documentation) {
public BaselineCompletionItem {
label = requireText(label, "label");
kind = Objects.requireNonNull(kind, "kind");
detail = normalize(detail);
documentation = normalize(documentation);
}
private static String requireText(
final String value,
final String field) {
final String candidate = normalize(value);
if (candidate.isEmpty()) {
throw new IllegalArgumentException(field + " must not be blank");
}
return candidate;
}
private static String normalize(final String value) {
return value == null ? "" : value.trim();
}
}

View File

@ -1,24 +1,20 @@
package p.studio.lsp.messages;
public enum LspSymbolKind {
public enum BaselineCompletionItemKind {
KEYWORD,
VARIABLE,
FIELD,
FUNCTION,
METHOD,
CONSTRUCTOR,
IF,
SWITCH,
FOR,
WHILE,
HANDLE,
GLOBAL,
CONST,
STRUCT,
CONTRACT,
HOST,
BUILTIN_TYPE,
SERVICE,
ERROR,
ENUM,
HOST,
CONTRACT,
CALLBACK,
IMPLEMENTS,
UNKNOWN
ENUM,
ERROR,
GLOBAL,
CONST
}

View File

@ -0,0 +1,12 @@
package p.studio.lsp.messages;
import java.util.List;
import java.util.Objects;
public record BaselineDocumentAnalysis(
List<BaselineDocumentIssue> issues) {
public BaselineDocumentAnalysis {
issues = List.copyOf(Objects.requireNonNull(issues, "issues"));
}
}

Some files were not shown because too many files have changed in this diff Show More