diff --git a/discussion/index.ndjson b/discussion/index.ndjson index b80e9891..7b3efb73 100644 --- a/discussion/index.ndjson +++ b/discussion/index.ndjson @@ -11,4 +11,4 @@ {"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":"open","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-03-31","tags":["studio","editor","workspace","write","read-only","vfs","frontend-boundary"],"agendas":[{"id":"AGD-0013","file":"AGD-0013-studio-editor-write-wave-supported-non-frontend-files.md","status":"accepted","created_at":"2026-03-31","updated_at":"2026-03-31"},{"id":"AGD-0014","file":"AGD-0014-studio-editor-frontend-edit-rights.md","status":"accepted","created_at":"2026-03-31","updated_at":"2026-03-31"}],"decisions":[{"id":"DEC-0010","file":"DEC-0010-studio-controlled-non-frontend-editor-write-wave.md","status":"accepted","created_at":"2026-03-31","updated_at":"2026-03-31","ref_agenda":"AGD-0013"},{"id":"DEC-0011","file":"DEC-0011-studio-frontend-read-only-minimum-lsp-phase.md","status":"accepted","created_at":"2026-03-31","updated_at":"2026-03-31","ref_agenda":"AGD-0014"}],"plans":[{"id":"PLN-0019","file":"PLN-0019-propagate-dec-0010-into-studio-and-vfs-specs.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0010"]},{"id":"PLN-0020","file":"PLN-0020-build-dec-0010-vfs-access-policy-and-save-core.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0010"]},{"id":"PLN-0021","file":"PLN-0021-integrate-dec-0010-editor-write-ui-and-workflow.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0010"]},{"id":"PLN-0022","file":"PLN-0022-propagate-dec-0011-into-studio-vfs-and-lsp-specs.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0011"]},{"id":"PLN-0023","file":"PLN-0023-scaffold-flat-packed-prometeu-lsp-api-and-session-seams.md","status":"review","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0011"]},{"id":"PLN-0024","file":"PLN-0024-implement-read-only-fe-diagnostics-symbols-and-definition.md","status":"review","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0011"]},{"id":"PLN-0025","file":"PLN-0025-implement-fe-semantic-highlight-consumption.md","status":"review","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0011"]}],"lessons":[]} +{"type":"discussion","id":"DSC-0013","status":"open","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-03-31","tags":["studio","editor","workspace","write","read-only","vfs","frontend-boundary"],"agendas":[{"id":"AGD-0013","file":"AGD-0013-studio-editor-write-wave-supported-non-frontend-files.md","status":"accepted","created_at":"2026-03-31","updated_at":"2026-03-31"},{"id":"AGD-0014","file":"AGD-0014-studio-editor-frontend-edit-rights.md","status":"accepted","created_at":"2026-03-31","updated_at":"2026-03-31"}],"decisions":[{"id":"DEC-0010","file":"DEC-0010-studio-controlled-non-frontend-editor-write-wave.md","status":"accepted","created_at":"2026-03-31","updated_at":"2026-03-31","ref_agenda":"AGD-0013"},{"id":"DEC-0011","file":"DEC-0011-studio-frontend-read-only-minimum-lsp-phase.md","status":"accepted","created_at":"2026-03-31","updated_at":"2026-03-31","ref_agenda":"AGD-0014"}],"plans":[{"id":"PLN-0019","file":"PLN-0019-propagate-dec-0010-into-studio-and-vfs-specs.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0010"]},{"id":"PLN-0020","file":"PLN-0020-build-dec-0010-vfs-access-policy-and-save-core.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0010"]},{"id":"PLN-0021","file":"PLN-0021-integrate-dec-0010-editor-write-ui-and-workflow.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0010"]},{"id":"PLN-0022","file":"PLN-0022-propagate-dec-0011-into-studio-vfs-and-lsp-specs.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0011"]},{"id":"PLN-0023","file":"PLN-0023-scaffold-flat-packed-prometeu-lsp-api-and-session-seams.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0011"]},{"id":"PLN-0024","file":"PLN-0024-implement-read-only-fe-diagnostics-symbols-and-definition.md","status":"review","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0011"]},{"id":"PLN-0025","file":"PLN-0025-implement-fe-semantic-highlight-consumption.md","status":"review","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0011"]}],"lessons":[]} diff --git a/discussion/workflow/plans/PLN-0023-scaffold-flat-packed-prometeu-lsp-api-and-session-seams.md b/discussion/workflow/plans/PLN-0023-scaffold-flat-packed-prometeu-lsp-api-and-session-seams.md index 67e3d920..8ae9c2cb 100644 --- a/discussion/workflow/plans/PLN-0023-scaffold-flat-packed-prometeu-lsp-api-and-session-seams.md +++ b/discussion/workflow/plans/PLN-0023-scaffold-flat-packed-prometeu-lsp-api-and-session-seams.md @@ -2,9 +2,9 @@ id: PLN-0023 ticket: studio-editor-write-wave-supported-non-frontend-files title: Scaffold flat-packed prometeu-lsp API and Studio session seams -status: review +status: done created: 2026-03-31 -completed: +completed: 2026-03-31 tags: [studio, lsp, api, packaging, session] --- diff --git a/prometeu-app/build.gradle.kts b/prometeu-app/build.gradle.kts index 34b687b9..8292bbaa 100644 --- a/prometeu-app/build.gradle.kts +++ b/prometeu-app/build.gradle.kts @@ -5,6 +5,8 @@ plugins { 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")) diff --git a/prometeu-app/src/main/java/p/studio/AppContainer.java b/prometeu-app/src/main/java/p/studio/AppContainer.java index 9afa8619..dcaa2b9f 100644 --- a/prometeu-app/src/main/java/p/studio/AppContainer.java +++ b/prometeu-app/src/main/java/p/studio/AppContainer.java @@ -2,6 +2,8 @@ package p.studio; import com.fasterxml.jackson.databind.ObjectMapper; import p.packer.Packer; +import p.lsp.PrometeuLspServiceFactory; +import p.lsp.v1.PrometeuLspV1ServiceFactory; import p.studio.events.StudioEventBus; import p.studio.events.StudioPackerEventAdapter; import p.studio.utilities.ThemeService; @@ -19,6 +21,7 @@ public final class AppContainer implements Container { private final ThemeService themeService; private final StudioEventBus studioEventBus; private final ObjectMapper mapper; + private final PrometeuLspServiceFactory prometeuLspServiceFactory; private final ProjectDocumentVfsFactory projectDocumentVfsFactory; private final EmbeddedPacker embeddedPacker; private final StudioBackgroundTasks backgroundTasks; @@ -28,6 +31,7 @@ public final class AppContainer implements Container { this.themeService = new ThemeService(); this.studioEventBus = new StudioEventBus(); this.mapper = new ObjectMapper(); + this.prometeuLspServiceFactory = new PrometeuLspV1ServiceFactory(); this.projectDocumentVfsFactory = new FilesystemProjectDocumentVfsFactory(); final ExecutorService backgroundExecutor = Executors.newFixedThreadPool(2, new StudioWorkerThreadFactory()); this.backgroundTasks = new StudioBackgroundTasks(backgroundExecutor); @@ -55,6 +59,11 @@ public final class AppContainer implements Container { return mapper; } + @Override + public PrometeuLspServiceFactory getPrometeuLspServiceFactory() { + return prometeuLspServiceFactory; + } + @Override public ProjectDocumentVfsFactory getProjectDocumentVfsFactory() { return projectDocumentVfsFactory; diff --git a/prometeu-lsp/prometeu-lsp-api/build.gradle.kts b/prometeu-lsp/prometeu-lsp-api/build.gradle.kts index a6775d6e..cc7fab83 100644 --- a/prometeu-lsp/prometeu-lsp-api/build.gradle.kts +++ b/prometeu-lsp/prometeu-lsp-api/build.gradle.kts @@ -1,3 +1,7 @@ plugins { id("gradle.java-library-conventions") } + +dependencies { + implementation(project(":prometeu-vfs:prometeu-vfs-api")) +} diff --git a/prometeu-lsp/prometeu-lsp-api/src/main/java/p/lsp/PrometeuLspProjectContext.java b/prometeu-lsp/prometeu-lsp-api/src/main/java/p/lsp/PrometeuLspProjectContext.java new file mode 100644 index 00000000..8c5753cf --- /dev/null +++ b/prometeu-lsp/prometeu-lsp-api/src/main/java/p/lsp/PrometeuLspProjectContext.java @@ -0,0 +1,16 @@ +package p.lsp; + +import java.nio.file.Path; +import java.util.Objects; + +public record PrometeuLspProjectContext( + String projectName, + String languageId, + Path rootPath) { + + public PrometeuLspProjectContext { + Objects.requireNonNull(projectName, "projectName"); + Objects.requireNonNull(languageId, "languageId"); + rootPath = Objects.requireNonNull(rootPath, "rootPath").toAbsolutePath().normalize(); + } +} diff --git a/prometeu-lsp/prometeu-lsp-api/src/main/java/p/lsp/PrometeuLspService.java b/prometeu-lsp/prometeu-lsp-api/src/main/java/p/lsp/PrometeuLspService.java new file mode 100644 index 00000000..86cd8bc8 --- /dev/null +++ b/prometeu-lsp/prometeu-lsp-api/src/main/java/p/lsp/PrometeuLspService.java @@ -0,0 +1,16 @@ +package p.lsp; + +import p.lsp.dtos.PrometeuLspSessionStateDTO; +import p.studio.vfs.ProjectDocumentVfs; + +public interface PrometeuLspService extends AutoCloseable { + PrometeuLspProjectContext projectContext(); + + ProjectDocumentVfs projectDocumentVfs(); + + PrometeuLspSessionStateDTO snapshot(); + + @Override + default void close() { + } +} diff --git a/prometeu-lsp/prometeu-lsp-api/src/main/java/p/lsp/PrometeuLspServiceFactory.java b/prometeu-lsp/prometeu-lsp-api/src/main/java/p/lsp/PrometeuLspServiceFactory.java new file mode 100644 index 00000000..14e6eb0d --- /dev/null +++ b/prometeu-lsp/prometeu-lsp-api/src/main/java/p/lsp/PrometeuLspServiceFactory.java @@ -0,0 +1,7 @@ +package p.lsp; + +import p.studio.vfs.ProjectDocumentVfs; + +public interface PrometeuLspServiceFactory { + PrometeuLspService open(PrometeuLspProjectContext projectContext, ProjectDocumentVfs projectDocumentVfs); +} diff --git a/prometeu-lsp/prometeu-lsp-api/src/main/java/p/lsp/dtos/PrometeuLspSessionStateDTO.java b/prometeu-lsp/prometeu-lsp-api/src/main/java/p/lsp/dtos/PrometeuLspSessionStateDTO.java new file mode 100644 index 00000000..19b773a2 --- /dev/null +++ b/prometeu-lsp/prometeu-lsp-api/src/main/java/p/lsp/dtos/PrometeuLspSessionStateDTO.java @@ -0,0 +1,13 @@ +package p.lsp.dtos; + +import java.util.List; +import java.util.Objects; + +public record PrometeuLspSessionStateDTO( + boolean semanticReadReady, + List declaredCapabilities) { + + public PrometeuLspSessionStateDTO { + declaredCapabilities = List.copyOf(Objects.requireNonNull(declaredCapabilities, "declaredCapabilities")); + } +} diff --git a/prometeu-lsp/prometeu-lsp-api/src/main/java/p/lsp/events/PrometeuLspEvent.java b/prometeu-lsp/prometeu-lsp-api/src/main/java/p/lsp/events/PrometeuLspEvent.java new file mode 100644 index 00000000..0ab44970 --- /dev/null +++ b/prometeu-lsp/prometeu-lsp-api/src/main/java/p/lsp/events/PrometeuLspEvent.java @@ -0,0 +1,13 @@ +package p.lsp.events; + +import java.util.Objects; + +public record PrometeuLspEvent( + PrometeuLspEventKind kind, + String message) { + + public PrometeuLspEvent { + Objects.requireNonNull(kind, "kind"); + Objects.requireNonNull(message, "message"); + } +} diff --git a/prometeu-lsp/prometeu-lsp-api/src/main/java/p/lsp/events/PrometeuLspEventKind.java b/prometeu-lsp/prometeu-lsp-api/src/main/java/p/lsp/events/PrometeuLspEventKind.java new file mode 100644 index 00000000..2019a9dc --- /dev/null +++ b/prometeu-lsp/prometeu-lsp-api/src/main/java/p/lsp/events/PrometeuLspEventKind.java @@ -0,0 +1,5 @@ +package p.lsp.events; + +public enum PrometeuLspEventKind { + SESSION_READY +} diff --git a/prometeu-lsp/prometeu-lsp-api/src/main/java/p/lsp/events/PrometeuLspEventSink.java b/prometeu-lsp/prometeu-lsp-api/src/main/java/p/lsp/events/PrometeuLspEventSink.java new file mode 100644 index 00000000..de9fc4b3 --- /dev/null +++ b/prometeu-lsp/prometeu-lsp-api/src/main/java/p/lsp/events/PrometeuLspEventSink.java @@ -0,0 +1,5 @@ +package p.lsp.events; + +public interface PrometeuLspEventSink { + void publish(PrometeuLspEvent event); +} diff --git a/prometeu-lsp/prometeu-lsp-api/src/main/java/p/lsp/messages/PrometeuLspAnalyzeDocumentRequest.java b/prometeu-lsp/prometeu-lsp-api/src/main/java/p/lsp/messages/PrometeuLspAnalyzeDocumentRequest.java new file mode 100644 index 00000000..89e40d55 --- /dev/null +++ b/prometeu-lsp/prometeu-lsp-api/src/main/java/p/lsp/messages/PrometeuLspAnalyzeDocumentRequest.java @@ -0,0 +1,12 @@ +package p.lsp.messages; + +import java.nio.file.Path; +import java.util.Objects; + +public record PrometeuLspAnalyzeDocumentRequest( + Path documentPath) { + + public PrometeuLspAnalyzeDocumentRequest { + documentPath = Objects.requireNonNull(documentPath, "documentPath").toAbsolutePath().normalize(); + } +} diff --git a/prometeu-lsp/prometeu-lsp-api/src/main/java/p/lsp/messages/PrometeuLspAnalyzeDocumentResult.java b/prometeu-lsp/prometeu-lsp-api/src/main/java/p/lsp/messages/PrometeuLspAnalyzeDocumentResult.java new file mode 100644 index 00000000..fc327953 --- /dev/null +++ b/prometeu-lsp/prometeu-lsp-api/src/main/java/p/lsp/messages/PrometeuLspAnalyzeDocumentResult.java @@ -0,0 +1,13 @@ +package p.lsp.messages; + +import p.lsp.dtos.PrometeuLspSessionStateDTO; + +import java.util.Objects; + +public record PrometeuLspAnalyzeDocumentResult( + PrometeuLspSessionStateDTO sessionState) { + + public PrometeuLspAnalyzeDocumentResult { + Objects.requireNonNull(sessionState, "sessionState"); + } +} diff --git a/prometeu-lsp/prometeu-lsp-v1/build.gradle.kts b/prometeu-lsp/prometeu-lsp-v1/build.gradle.kts index a6775d6e..d18b3da7 100644 --- a/prometeu-lsp/prometeu-lsp-v1/build.gradle.kts +++ b/prometeu-lsp/prometeu-lsp-v1/build.gradle.kts @@ -1,3 +1,8 @@ plugins { id("gradle.java-library-conventions") } + +dependencies { + implementation(project(":prometeu-lsp:prometeu-lsp-api")) + implementation(project(":prometeu-vfs:prometeu-vfs-api")) +} diff --git a/prometeu-lsp/prometeu-lsp-v1/src/main/java/p/lsp/v1/PrometeuLspV1ServiceFactory.java b/prometeu-lsp/prometeu-lsp-v1/src/main/java/p/lsp/v1/PrometeuLspV1ServiceFactory.java new file mode 100644 index 00000000..09f786be --- /dev/null +++ b/prometeu-lsp/prometeu-lsp-v1/src/main/java/p/lsp/v1/PrometeuLspV1ServiceFactory.java @@ -0,0 +1,20 @@ +package p.lsp.v1; + +import p.lsp.PrometeuLspProjectContext; +import p.lsp.PrometeuLspService; +import p.lsp.PrometeuLspServiceFactory; +import p.lsp.v1.internal.PrometeuLspV1Service; +import p.studio.vfs.ProjectDocumentVfs; + +import java.util.Objects; + +public final class PrometeuLspV1ServiceFactory implements PrometeuLspServiceFactory { + @Override + public PrometeuLspService open( + final PrometeuLspProjectContext projectContext, + final ProjectDocumentVfs projectDocumentVfs) { + return new PrometeuLspV1Service( + Objects.requireNonNull(projectContext, "projectContext"), + Objects.requireNonNull(projectDocumentVfs, "projectDocumentVfs")); + } +} diff --git a/prometeu-lsp/prometeu-lsp-v1/src/main/java/p/lsp/v1/internal/PrometeuLspV1Service.java b/prometeu-lsp/prometeu-lsp-v1/src/main/java/p/lsp/v1/internal/PrometeuLspV1Service.java new file mode 100644 index 00000000..11ae2b7c --- /dev/null +++ b/prometeu-lsp/prometeu-lsp-v1/src/main/java/p/lsp/v1/internal/PrometeuLspV1Service.java @@ -0,0 +1,38 @@ +package p.lsp.v1.internal; + +import p.lsp.PrometeuLspProjectContext; +import p.lsp.PrometeuLspService; +import p.lsp.dtos.PrometeuLspSessionStateDTO; +import p.studio.vfs.ProjectDocumentVfs; + +import java.util.List; +import java.util.Objects; + +public final class PrometeuLspV1Service implements PrometeuLspService { + private final PrometeuLspProjectContext projectContext; + private final ProjectDocumentVfs projectDocumentVfs; + + public PrometeuLspV1Service( + final PrometeuLspProjectContext projectContext, + final ProjectDocumentVfs projectDocumentVfs) { + this.projectContext = Objects.requireNonNull(projectContext, "projectContext"); + this.projectDocumentVfs = Objects.requireNonNull(projectDocumentVfs, "projectDocumentVfs"); + } + + @Override + public PrometeuLspProjectContext projectContext() { + return projectContext; + } + + @Override + public ProjectDocumentVfs projectDocumentVfs() { + return projectDocumentVfs; + } + + @Override + public PrometeuLspSessionStateDTO snapshot() { + return new PrometeuLspSessionStateDTO( + true, + List.of("diagnostics", "symbols", "definition", "highlight")); + } +} diff --git a/prometeu-studio/build.gradle.kts b/prometeu-studio/build.gradle.kts index d589a623..070a27c1 100644 --- a/prometeu-studio/build.gradle.kts +++ b/prometeu-studio/build.gradle.kts @@ -5,6 +5,7 @@ plugins { dependencies { implementation(project(":prometeu-infra")) + implementation(project(":prometeu-lsp:prometeu-lsp-api")) implementation(project(":prometeu-vfs:prometeu-vfs-api")) implementation(project(":prometeu-packer:prometeu-packer-api")) implementation(project(":prometeu-compiler:prometeu-compiler-core")) diff --git a/prometeu-studio/src/main/java/p/studio/Container.java b/prometeu-studio/src/main/java/p/studio/Container.java index 0155c001..7447d7d3 100644 --- a/prometeu-studio/src/main/java/p/studio/Container.java +++ b/prometeu-studio/src/main/java/p/studio/Container.java @@ -1,6 +1,7 @@ package p.studio; import com.fasterxml.jackson.databind.ObjectMapper; +import p.lsp.PrometeuLspServiceFactory; import p.studio.events.StudioEventBus; import p.studio.utilities.ThemeService; import p.studio.utilities.i18n.I18nService; @@ -18,6 +19,8 @@ public interface Container { ObjectMapper getMapper(); + PrometeuLspServiceFactory getPrometeuLspServiceFactory(); + ProjectDocumentVfsFactory getProjectDocumentVfsFactory(); EmbeddedPacker getPacker(); @@ -63,6 +66,10 @@ public interface Container { return current().getMapper(); } + static PrometeuLspServiceFactory prometeuLspServiceFactory() { + return current().getPrometeuLspServiceFactory(); + } + static ProjectDocumentVfsFactory projectDocumentVfsFactory() { return current().getProjectDocumentVfsFactory(); } diff --git a/prometeu-studio/src/main/java/p/studio/projectsessions/StudioProjectSession.java b/prometeu-studio/src/main/java/p/studio/projectsessions/StudioProjectSession.java index 0bfbea1f..d616e8de 100644 --- a/prometeu-studio/src/main/java/p/studio/projectsessions/StudioProjectSession.java +++ b/prometeu-studio/src/main/java/p/studio/projectsessions/StudioProjectSession.java @@ -1,5 +1,6 @@ package p.studio.projectsessions; +import p.lsp.PrometeuLspService; import p.studio.projects.ProjectReference; import p.studio.vfs.ProjectDocumentVfs; @@ -7,13 +8,16 @@ import java.util.Objects; public final class StudioProjectSession implements AutoCloseable { private final ProjectReference projectReference; + private final PrometeuLspService prometeuLspService; private final ProjectDocumentVfs projectDocumentVfs; private boolean closed; public StudioProjectSession( final ProjectReference projectReference, + final PrometeuLspService prometeuLspService, final ProjectDocumentVfs projectDocumentVfs) { this.projectReference = Objects.requireNonNull(projectReference, "projectReference"); + this.prometeuLspService = Objects.requireNonNull(prometeuLspService, "prometeuLspService"); this.projectDocumentVfs = Objects.requireNonNull(projectDocumentVfs, "projectDocumentVfs"); } @@ -25,12 +29,17 @@ public final class StudioProjectSession implements AutoCloseable { return projectDocumentVfs; } + public PrometeuLspService prometeuLspService() { + return prometeuLspService; + } + @Override public void close() { if (closed) { return; } closed = true; + prometeuLspService.close(); projectDocumentVfs.close(); } } diff --git a/prometeu-studio/src/main/java/p/studio/projectsessions/StudioProjectSessionFactory.java b/prometeu-studio/src/main/java/p/studio/projectsessions/StudioProjectSessionFactory.java index 9b4820c1..a799a76d 100644 --- a/prometeu-studio/src/main/java/p/studio/projectsessions/StudioProjectSessionFactory.java +++ b/prometeu-studio/src/main/java/p/studio/projectsessions/StudioProjectSessionFactory.java @@ -1,21 +1,37 @@ package p.studio.projectsessions; +import p.lsp.PrometeuLspProjectContext; +import p.lsp.PrometeuLspServiceFactory; import p.studio.projects.ProjectReference; +import p.studio.vfs.ProjectDocumentVfs; import p.studio.vfs.ProjectDocumentVfsFactory; import java.util.Objects; public final class StudioProjectSessionFactory { + private final PrometeuLspServiceFactory prometeuLspServiceFactory; private final ProjectDocumentVfsFactory projectDocumentVfsFactory; - public StudioProjectSessionFactory(final ProjectDocumentVfsFactory projectDocumentVfsFactory) { + public StudioProjectSessionFactory( + final PrometeuLspServiceFactory prometeuLspServiceFactory, + final ProjectDocumentVfsFactory projectDocumentVfsFactory) { + this.prometeuLspServiceFactory = Objects.requireNonNull(prometeuLspServiceFactory, "prometeuLspServiceFactory"); this.projectDocumentVfsFactory = Objects.requireNonNull(projectDocumentVfsFactory, "projectDocumentVfsFactory"); } public StudioProjectSession open(final ProjectReference projectReference) { final ProjectReference target = Objects.requireNonNull(projectReference, "projectReference"); + final ProjectDocumentVfs projectDocumentVfs = projectDocumentVfsFactory.open(target.toVfsProjectContext()); return new StudioProjectSession( target, - projectDocumentVfsFactory.open(target.toVfsProjectContext())); + prometeuLspServiceFactory.open(lspProjectContext(target), projectDocumentVfs), + projectDocumentVfs); + } + + private PrometeuLspProjectContext lspProjectContext(final ProjectReference projectReference) { + return new PrometeuLspProjectContext( + projectReference.name(), + projectReference.languageId(), + projectReference.rootPath()); } } diff --git a/prometeu-studio/src/main/java/p/studio/window/StudioWindowCoordinator.java b/prometeu-studio/src/main/java/p/studio/window/StudioWindowCoordinator.java index 027236dc..7f9c8931 100644 --- a/prometeu-studio/src/main/java/p/studio/window/StudioWindowCoordinator.java +++ b/prometeu-studio/src/main/java/p/studio/window/StudioWindowCoordinator.java @@ -37,7 +37,9 @@ public final class StudioWindowCoordinator { this.projectCatalogService = new ProjectCatalogService(resolveDefaultProjectsRoot()); this.knownProjectsService = new KnownProjectsService(resolveKnownProjectsStorage(), projectCatalogService); this.windowStateService = new WindowStateService(resolveWindowStateStorage()); - this.projectSessionFactory = new StudioProjectSessionFactory(Container.projectDocumentVfsFactory()); + this.projectSessionFactory = new StudioProjectSessionFactory( + Container.prometeuLspServiceFactory(), + Container.projectDocumentVfsFactory()); this.launcherView = new ProjectLauncherView( knownProjectsService, projectCatalogService, diff --git a/prometeu-studio/src/test/java/p/studio/projectsessions/StudioProjectSessionFactoryTest.java b/prometeu-studio/src/test/java/p/studio/projectsessions/StudioProjectSessionFactoryTest.java index 7d0bb181..9fc472be 100644 --- a/prometeu-studio/src/test/java/p/studio/projectsessions/StudioProjectSessionFactoryTest.java +++ b/prometeu-studio/src/test/java/p/studio/projectsessions/StudioProjectSessionFactoryTest.java @@ -1,6 +1,10 @@ package p.studio.projectsessions; import org.junit.jupiter.api.Test; +import p.lsp.PrometeuLspProjectContext; +import p.lsp.PrometeuLspService; +import p.lsp.PrometeuLspServiceFactory; +import p.lsp.dtos.PrometeuLspSessionStateDTO; import p.studio.projects.ProjectReference; import p.studio.vfs.ProjectDocumentVfs; import p.studio.vfs.ProjectDocumentVfsFactory; @@ -17,8 +21,9 @@ import static org.junit.jupiter.api.Assertions.assertSame; final class StudioProjectSessionFactoryTest { @Test void openCreatesProjectSessionBackedByProjectScopedVfs() { + final RecordingLspFactory lspFactory = new RecordingLspFactory(); final RecordingVfsFactory vfsFactory = new RecordingVfsFactory(); - final StudioProjectSessionFactory sessionFactory = new StudioProjectSessionFactory(vfsFactory); + final StudioProjectSessionFactory sessionFactory = new StudioProjectSessionFactory(lspFactory, vfsFactory); final ProjectReference projectReference = new ProjectReference( "Example", "1.0.0", @@ -30,9 +35,13 @@ final class StudioProjectSessionFactoryTest { assertSame(projectReference, session.projectReference()); assertSame(vfsFactory.vfs, session.projectDocumentVfs()); + assertSame(lspFactory.service, session.prometeuLspService()); assertEquals("Example", vfsFactory.capturedContext.projectName()); assertEquals("pbs", vfsFactory.capturedContext.languageId()); assertEquals(projectReference.rootPath().toAbsolutePath().normalize(), vfsFactory.capturedContext.rootPath()); + assertEquals("Example", lspFactory.capturedContext.projectName()); + assertEquals("pbs", lspFactory.capturedContext.languageId()); + assertSame(vfsFactory.vfs, lspFactory.capturedVfs); } private static final class RecordingVfsFactory implements ProjectDocumentVfsFactory { @@ -46,6 +55,21 @@ final class StudioProjectSessionFactoryTest { } } + private static final class RecordingLspFactory implements PrometeuLspServiceFactory { + private PrometeuLspProjectContext capturedContext; + private ProjectDocumentVfs capturedVfs; + private final PrometeuLspService service = new NoOpPrometeuLspService(); + + @Override + public PrometeuLspService open( + final PrometeuLspProjectContext projectContext, + final ProjectDocumentVfs projectDocumentVfs) { + this.capturedContext = projectContext; + this.capturedVfs = projectDocumentVfs; + return service; + } + } + private static final class NoOpProjectDocumentVfs implements ProjectDocumentVfs { @Override public VfsProjectContext projectContext() { @@ -72,4 +96,21 @@ final class StudioProjectSessionFactoryTest { throw new UnsupportedOperationException(); } } + + private static final class NoOpPrometeuLspService implements PrometeuLspService { + @Override + public PrometeuLspProjectContext projectContext() { + throw new UnsupportedOperationException(); + } + + @Override + public ProjectDocumentVfs projectDocumentVfs() { + throw new UnsupportedOperationException(); + } + + @Override + public PrometeuLspSessionStateDTO snapshot() { + throw new UnsupportedOperationException(); + } + } } diff --git a/prometeu-studio/src/test/java/p/studio/projectsessions/StudioProjectSessionTest.java b/prometeu-studio/src/test/java/p/studio/projectsessions/StudioProjectSessionTest.java index b8d7165f..9139160b 100644 --- a/prometeu-studio/src/test/java/p/studio/projectsessions/StudioProjectSessionTest.java +++ b/prometeu-studio/src/test/java/p/studio/projectsessions/StudioProjectSessionTest.java @@ -1,6 +1,9 @@ package p.studio.projectsessions; import org.junit.jupiter.api.Test; +import p.lsp.PrometeuLspProjectContext; +import p.lsp.PrometeuLspService; +import p.lsp.dtos.PrometeuLspSessionStateDTO; import p.studio.projects.ProjectReference; import p.studio.vfs.ProjectDocumentVfs; import p.studio.vfs.VfsDocumentOpenResult; @@ -14,13 +17,15 @@ import static org.junit.jupiter.api.Assertions.assertEquals; final class StudioProjectSessionTest { @Test - void closeDelegatesToUnderlyingVfsOnlyOnce() { + void closeDelegatesToUnderlyingServicesOnlyOnce() { final CountingProjectDocumentVfs vfs = new CountingProjectDocumentVfs(); - final StudioProjectSession session = new StudioProjectSession(projectReference(), vfs); + final CountingPrometeuLspService lsp = new CountingPrometeuLspService(vfs); + final StudioProjectSession session = new StudioProjectSession(projectReference(), lsp, vfs); session.close(); session.close(); + assertEquals(1, lsp.closeCalls); assertEquals(1, vfs.closeCalls); } @@ -61,4 +66,33 @@ final class StudioProjectSessionTest { closeCalls++; } } + + private static final class CountingPrometeuLspService implements PrometeuLspService { + private final ProjectDocumentVfs projectDocumentVfs; + private int closeCalls; + + private CountingPrometeuLspService(final ProjectDocumentVfs projectDocumentVfs) { + this.projectDocumentVfs = projectDocumentVfs; + } + + @Override + public PrometeuLspProjectContext projectContext() { + throw new UnsupportedOperationException(); + } + + @Override + public ProjectDocumentVfs projectDocumentVfs() { + return projectDocumentVfs; + } + + @Override + public PrometeuLspSessionStateDTO snapshot() { + throw new UnsupportedOperationException(); + } + + @Override + public void close() { + closeCalls++; + } + } }