dev/prometeu-lsp #8
11
README.md
11
README.md
@ -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
|
||||
|
||||
@ -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"}]}
|
||||
@ -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":[]}
|
||||
35
discussion/.backups/index.ndjson.20260507-142320.bak
Normal file
35
discussion/.backups/index.ndjson.20260507-142320.bak
Normal 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"}]}
|
||||
35
discussion/.backups/index.ndjson.20260508-091553.bak
Normal file
35
discussion/.backups/index.ndjson.20260508-091553.bak
Normal 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"}]}
|
||||
@ -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"}]}
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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`.
|
||||
|
||||
@ -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
|
||||
@ -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.
|
||||
@ -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
|
||||
@ -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.
|
||||
@ -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.
|
||||
@ -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.
|
||||
@ -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.
|
||||
@ -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.
|
||||
@ -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.
|
||||
@ -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.
|
||||
@ -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.
|
||||
|
||||
|
||||
@ -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.
|
||||
@ -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.
|
||||
@ -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.
|
||||
@ -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`;
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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" }
|
||||
|
||||
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
@ -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)));
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()));
|
||||
}
|
||||
}
|
||||
@ -3,5 +3,4 @@ plugins {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":prometeu-vfs:prometeu-vfs-api"))
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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"));
|
||||
}
|
||||
}
|
||||
@ -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"));
|
||||
}
|
||||
}
|
||||
@ -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"));
|
||||
}
|
||||
}
|
||||
@ -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"));
|
||||
}
|
||||
}
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
package p.studio.lsp.events;
|
||||
|
||||
public enum LspEventKind {
|
||||
SESSION_READY
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
package p.studio.lsp.events;
|
||||
|
||||
public interface LspEventSink {
|
||||
void publish(LspEvent event);
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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"));
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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"));
|
||||
}
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
package p.studio.lsp.messages;
|
||||
|
||||
public enum LspDiagnosticSeverity {
|
||||
ERROR,
|
||||
WARNING
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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"));
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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"));
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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"));
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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
Loading…
x
Reference in New Issue
Block a user