implements PLN-0066
This commit is contained in:
parent
b0073cd7bd
commit
18dc64e36c
@ -1,5 +1,5 @@
|
|||||||
{"type":"meta","next_id":{"DSC":33,"AGD":36,"DEC":33,"PLN":68,"LSN":47,"CLSN":1}}
|
{"type":"meta","next_id":{"DSC":33,"AGD":36,"DEC":33,"PLN":68,"LSN":47,"CLSN":1}}
|
||||||
{"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":"open","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":"open","created_at":"2026-05-05","updated_at":"2026-05-05","ref_decisions":["DEC-0032"]}],"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":"open","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-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-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-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"}]}
|
||||||
|
|||||||
@ -2,9 +2,9 @@
|
|||||||
id: PLN-0066
|
id: PLN-0066
|
||||||
ticket: studio-new-lsp-api-and-v1-boundary
|
ticket: studio-new-lsp-api-and-v1-boundary
|
||||||
title: Project-Scoped LSP Server Lifecycle in Studio
|
title: Project-Scoped LSP Server Lifecycle in Studio
|
||||||
status: open
|
status: done
|
||||||
created: 2026-05-05
|
created: 2026-05-05
|
||||||
completed:
|
completed: 2026-05-05
|
||||||
tags: [studio, lsp, lifecycle, projects, boot, shutdown]
|
tags: [studio, lsp, lifecycle, projects, boot, shutdown]
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -85,11 +85,11 @@ These surfaces already own project startup and teardown flow and therefore are t
|
|||||||
|
|
||||||
## Acceptance Criteria
|
## Acceptance Criteria
|
||||||
|
|
||||||
- [ ] Opening a Studio project boots an LSP server for that project.
|
- [x] Opening a Studio project boots an LSP server for that project.
|
||||||
- [ ] Closing the project shuts down that server.
|
- [x] Closing the project shuts down that server.
|
||||||
- [ ] The lifecycle is owned by project session boundaries, not global app startup.
|
- [x] The lifecycle is owned by project session boundaries, not global app startup.
|
||||||
- [ ] Existing non-LSP project flows remain operational.
|
- [x] Existing non-LSP project flows remain operational.
|
||||||
- [ ] The current dumb connectivity workflow remains usable as a temporary implementation phase.
|
- [x] The current dumb connectivity workflow remains usable as a temporary implementation phase.
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,8 @@ plugins {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":prometeu-infra"))
|
implementation(project(":prometeu-infra"))
|
||||||
|
implementation(project(":prometeu-lsp:prometeu-lsp-api"))
|
||||||
|
implementation(project(":prometeu-lsp:prometeu-lsp-v1"))
|
||||||
implementation(project(":prometeu-packer:prometeu-packer-api"))
|
implementation(project(":prometeu-packer:prometeu-packer-api"))
|
||||||
implementation(project(":prometeu-packer:prometeu-packer-v1"))
|
implementation(project(":prometeu-packer:prometeu-packer-v1"))
|
||||||
implementation(project(":prometeu-studio"))
|
implementation(project(":prometeu-studio"))
|
||||||
|
|||||||
@ -2,8 +2,12 @@ package p.studio;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import p.packer.Packer;
|
import p.packer.Packer;
|
||||||
|
import p.studio.lsp.api.LspServerLifecycle;
|
||||||
import p.studio.lsp.events.StudioEventBus;
|
import p.studio.lsp.events.StudioEventBus;
|
||||||
import p.studio.lsp.events.StudioPackerEventAdapter;
|
import p.studio.lsp.events.StudioPackerEventAdapter;
|
||||||
|
import p.studio.lsp.v1.bootstrap.LspV1ServerLifecycle;
|
||||||
|
import p.studio.lsp.v1.compiler.CompilerLanguageServiceBridge;
|
||||||
|
import p.studio.lsp.v1.host.TcpLspServerHostFactory;
|
||||||
import p.studio.utilities.ThemeService;
|
import p.studio.utilities.ThemeService;
|
||||||
import p.studio.utilities.i18n.I18nService;
|
import p.studio.utilities.i18n.I18nService;
|
||||||
|
|
||||||
@ -19,6 +23,7 @@ public final class AppContainer implements Container {
|
|||||||
private final ObjectMapper mapper;
|
private final ObjectMapper mapper;
|
||||||
private final EmbeddedPacker embeddedPacker;
|
private final EmbeddedPacker embeddedPacker;
|
||||||
private final StudioBackgroundTasks backgroundTasks;
|
private final StudioBackgroundTasks backgroundTasks;
|
||||||
|
private final LspServerLifecycle lspServerLifecycle;
|
||||||
|
|
||||||
public AppContainer() {
|
public AppContainer() {
|
||||||
this.i18nService = new I18nService();
|
this.i18nService = new I18nService();
|
||||||
@ -29,6 +34,9 @@ public final class AppContainer implements Container {
|
|||||||
this.backgroundTasks = new StudioBackgroundTasks(backgroundExecutor);
|
this.backgroundTasks = new StudioBackgroundTasks(backgroundExecutor);
|
||||||
final Packer packer = Packer.bootstrap(this.mapper, new StudioPackerEventAdapter(studioEventBus));
|
final Packer packer = Packer.bootstrap(this.mapper, new StudioPackerEventAdapter(studioEventBus));
|
||||||
this.embeddedPacker = new EmbeddedPacker(packer.workspaceService(), packer::close);
|
this.embeddedPacker = new EmbeddedPacker(packer.workspaceService(), packer::close);
|
||||||
|
this.lspServerLifecycle = new LspV1ServerLifecycle(
|
||||||
|
new TcpLspServerHostFactory(),
|
||||||
|
new CompilerLanguageServiceBridge());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -61,6 +69,11 @@ public final class AppContainer implements Container {
|
|||||||
return backgroundTasks;
|
return backgroundTasks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LspServerLifecycle getLspServerLifecycle() {
|
||||||
|
return lspServerLifecycle;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void shutdownContainer() {
|
public void shutdownContainer() {
|
||||||
embeddedPacker.shutdown();
|
embeddedPacker.shutdown();
|
||||||
|
|||||||
@ -5,6 +5,7 @@ plugins {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":prometeu-infra"))
|
implementation(project(":prometeu-infra"))
|
||||||
|
implementation(project(":prometeu-lsp:prometeu-lsp-api"))
|
||||||
implementation(project(":prometeu-packer:prometeu-packer-api"))
|
implementation(project(":prometeu-packer:prometeu-packer-api"))
|
||||||
implementation(project(":prometeu-compiler:prometeu-compiler-core"))
|
implementation(project(":prometeu-compiler:prometeu-compiler-core"))
|
||||||
implementation(project(":prometeu-compiler:prometeu-build-pipeline"))
|
implementation(project(":prometeu-compiler:prometeu-build-pipeline"))
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package p.studio;
|
package p.studio;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import p.studio.lsp.api.LspServerLifecycle;
|
||||||
import p.studio.lsp.events.StudioEventBus;
|
import p.studio.lsp.events.StudioEventBus;
|
||||||
import p.studio.utilities.ThemeService;
|
import p.studio.utilities.ThemeService;
|
||||||
import p.studio.utilities.i18n.I18nService;
|
import p.studio.utilities.i18n.I18nService;
|
||||||
@ -21,6 +22,8 @@ public interface Container {
|
|||||||
|
|
||||||
StudioBackgroundTasks getBackgroundTasks();
|
StudioBackgroundTasks getBackgroundTasks();
|
||||||
|
|
||||||
|
LspServerLifecycle getLspServerLifecycle();
|
||||||
|
|
||||||
void shutdownContainer();
|
void shutdownContainer();
|
||||||
|
|
||||||
static void install(Container container) {
|
static void install(Container container) {
|
||||||
@ -68,6 +71,10 @@ public interface Container {
|
|||||||
return current().getBackgroundTasks();
|
return current().getBackgroundTasks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static LspServerLifecycle lspServerLifecycle() {
|
||||||
|
return current().getLspServerLifecycle();
|
||||||
|
}
|
||||||
|
|
||||||
static void shutdown() {
|
static void shutdown() {
|
||||||
final Container container = Holder.CURRENT.get();
|
final Container container = Holder.CURRENT.get();
|
||||||
if (container != null) {
|
if (container != null) {
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
package p.studio.projectsessions;
|
package p.studio.projectsessions;
|
||||||
|
|
||||||
import p.studio.execution.StudioExecutionSessionService;
|
import p.studio.execution.StudioExecutionSessionService;
|
||||||
|
import p.studio.lsp.api.LspProjectContext;
|
||||||
|
import p.studio.lsp.api.LspServerHandle;
|
||||||
|
import p.studio.lsp.api.LspServerLifecycle;
|
||||||
import p.studio.projectstate.ProjectLocalStudioSetup;
|
import p.studio.projectstate.ProjectLocalStudioSetup;
|
||||||
import p.studio.projectstate.ProjectLocalStudioState;
|
import p.studio.projectstate.ProjectLocalStudioState;
|
||||||
import p.studio.projectstate.ProjectLocalStudioStateService;
|
import p.studio.projectstate.ProjectLocalStudioStateService;
|
||||||
@ -13,6 +16,8 @@ public final class StudioProjectSession implements AutoCloseable {
|
|||||||
private final ProjectLocalStudioStateService projectLocalStudioStateService;
|
private final ProjectLocalStudioStateService projectLocalStudioStateService;
|
||||||
private final ProjectLocalStudioSetup projectLocalStudioSetup;
|
private final ProjectLocalStudioSetup projectLocalStudioSetup;
|
||||||
private final StudioExecutionSessionService executionSessionService;
|
private final StudioExecutionSessionService executionSessionService;
|
||||||
|
private final LspServerLifecycle lspServerLifecycle;
|
||||||
|
private final LspServerHandle lspServerHandle;
|
||||||
private ProjectLocalStudioState projectLocalStudioState;
|
private ProjectLocalStudioState projectLocalStudioState;
|
||||||
private boolean closed;
|
private boolean closed;
|
||||||
|
|
||||||
@ -23,6 +28,8 @@ public final class StudioProjectSession implements AutoCloseable {
|
|||||||
new ProjectLocalStudioStateService(),
|
new ProjectLocalStudioStateService(),
|
||||||
ProjectLocalStudioSetup.defaults(),
|
ProjectLocalStudioSetup.defaults(),
|
||||||
new StudioExecutionSessionService(),
|
new StudioExecutionSessionService(),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
ProjectLocalStudioState.defaults());
|
ProjectLocalStudioState.defaults());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,11 +38,15 @@ public final class StudioProjectSession implements AutoCloseable {
|
|||||||
final ProjectLocalStudioStateService projectLocalStudioStateService,
|
final ProjectLocalStudioStateService projectLocalStudioStateService,
|
||||||
final ProjectLocalStudioSetup projectLocalStudioSetup,
|
final ProjectLocalStudioSetup projectLocalStudioSetup,
|
||||||
final StudioExecutionSessionService executionSessionService,
|
final StudioExecutionSessionService executionSessionService,
|
||||||
|
final LspServerLifecycle lspServerLifecycle,
|
||||||
|
final LspServerHandle lspServerHandle,
|
||||||
final ProjectLocalStudioState projectLocalStudioState) {
|
final ProjectLocalStudioState projectLocalStudioState) {
|
||||||
this.projectReference = Objects.requireNonNull(projectReference, "projectReference");
|
this.projectReference = Objects.requireNonNull(projectReference, "projectReference");
|
||||||
this.projectLocalStudioStateService = Objects.requireNonNull(projectLocalStudioStateService, "projectLocalStudioStateService");
|
this.projectLocalStudioStateService = Objects.requireNonNull(projectLocalStudioStateService, "projectLocalStudioStateService");
|
||||||
this.projectLocalStudioSetup = Objects.requireNonNull(projectLocalStudioSetup, "projectLocalStudioSetup");
|
this.projectLocalStudioSetup = Objects.requireNonNull(projectLocalStudioSetup, "projectLocalStudioSetup");
|
||||||
this.executionSessionService = Objects.requireNonNull(executionSessionService, "executionSessionService");
|
this.executionSessionService = Objects.requireNonNull(executionSessionService, "executionSessionService");
|
||||||
|
this.lspServerLifecycle = lspServerLifecycle;
|
||||||
|
this.lspServerHandle = lspServerHandle;
|
||||||
this.projectLocalStudioState = Objects.requireNonNull(projectLocalStudioState, "projectLocalStudioState");
|
this.projectLocalStudioState = Objects.requireNonNull(projectLocalStudioState, "projectLocalStudioState");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,6 +66,10 @@ public final class StudioProjectSession implements AutoCloseable {
|
|||||||
return executionSessionService;
|
return executionSessionService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LspServerHandle lspServerHandle() {
|
||||||
|
return lspServerHandle;
|
||||||
|
}
|
||||||
|
|
||||||
public void replaceProjectLocalStudioState(final ProjectLocalStudioState nextProjectLocalStudioState) {
|
public void replaceProjectLocalStudioState(final ProjectLocalStudioState nextProjectLocalStudioState) {
|
||||||
this.projectLocalStudioState = Objects.requireNonNull(nextProjectLocalStudioState, "nextProjectLocalStudioState");
|
this.projectLocalStudioState = Objects.requireNonNull(nextProjectLocalStudioState, "nextProjectLocalStudioState");
|
||||||
}
|
}
|
||||||
@ -76,8 +91,12 @@ public final class StudioProjectSession implements AutoCloseable {
|
|||||||
failure = runtimeException;
|
failure = runtimeException;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (failure != null) {
|
shutdownLspServer();
|
||||||
throw failure;
|
} catch (RuntimeException runtimeException) {
|
||||||
|
if (failure == null) {
|
||||||
|
failure = runtimeException;
|
||||||
|
} else {
|
||||||
|
failure.addSuppressed(runtimeException);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (failure != null) {
|
if (failure != null) {
|
||||||
@ -85,4 +104,12 @@ public final class StudioProjectSession implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void shutdownLspServer() {
|
||||||
|
if (lspServerLifecycle == null || lspServerHandle == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final LspProjectContext projectContext = lspServerHandle.project();
|
||||||
|
lspServerLifecycle.shutdownServer(projectContext);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
package p.studio.projectsessions;
|
package p.studio.projectsessions;
|
||||||
|
|
||||||
|
import p.studio.lsp.api.LspProjectContext;
|
||||||
|
import p.studio.lsp.api.LspServerBootRequest;
|
||||||
|
import p.studio.lsp.api.LspServerHandle;
|
||||||
|
import p.studio.lsp.api.LspServerLifecycle;
|
||||||
import p.studio.execution.StudioExecutionSessionService;
|
import p.studio.execution.StudioExecutionSessionService;
|
||||||
import p.studio.projectstate.ProjectLocalStudioSetupService;
|
import p.studio.projectstate.ProjectLocalStudioSetupService;
|
||||||
import p.studio.projectstate.ProjectLocalStudioStateService;
|
import p.studio.projectstate.ProjectLocalStudioStateService;
|
||||||
@ -10,27 +14,44 @@ import java.util.Objects;
|
|||||||
public final class StudioProjectSessionFactory {
|
public final class StudioProjectSessionFactory {
|
||||||
private final ProjectLocalStudioStateService projectLocalStudioStateService;
|
private final ProjectLocalStudioStateService projectLocalStudioStateService;
|
||||||
private final ProjectLocalStudioSetupService projectLocalStudioSetupService;
|
private final ProjectLocalStudioSetupService projectLocalStudioSetupService;
|
||||||
|
private final LspServerLifecycle lspServerLifecycle;
|
||||||
|
|
||||||
public StudioProjectSessionFactory() {
|
public StudioProjectSessionFactory(final LspServerLifecycle lspServerLifecycle) {
|
||||||
this(
|
this(
|
||||||
new ProjectLocalStudioStateService(),
|
new ProjectLocalStudioStateService(),
|
||||||
new ProjectLocalStudioSetupService());
|
new ProjectLocalStudioSetupService(),
|
||||||
|
Objects.requireNonNull(lspServerLifecycle, "lspServerLifecycle"));
|
||||||
}
|
}
|
||||||
|
|
||||||
StudioProjectSessionFactory(
|
StudioProjectSessionFactory(
|
||||||
final ProjectLocalStudioStateService projectLocalStudioStateService,
|
final ProjectLocalStudioStateService projectLocalStudioStateService,
|
||||||
final ProjectLocalStudioSetupService projectLocalStudioSetupService) {
|
final ProjectLocalStudioSetupService projectLocalStudioSetupService,
|
||||||
|
final LspServerLifecycle lspServerLifecycle) {
|
||||||
this.projectLocalStudioStateService = Objects.requireNonNull(projectLocalStudioStateService, "projectLocalStudioStateService");
|
this.projectLocalStudioStateService = Objects.requireNonNull(projectLocalStudioStateService, "projectLocalStudioStateService");
|
||||||
this.projectLocalStudioSetupService = Objects.requireNonNull(projectLocalStudioSetupService, "projectLocalStudioSetupService");
|
this.projectLocalStudioSetupService = Objects.requireNonNull(projectLocalStudioSetupService, "projectLocalStudioSetupService");
|
||||||
|
this.lspServerLifecycle = Objects.requireNonNull(lspServerLifecycle, "lspServerLifecycle");
|
||||||
}
|
}
|
||||||
|
|
||||||
public StudioProjectSession open(final ProjectReference projectReference) {
|
public StudioProjectSession open(final ProjectReference projectReference) {
|
||||||
final ProjectReference target = Objects.requireNonNull(projectReference, "projectReference");
|
final ProjectReference target = Objects.requireNonNull(projectReference, "projectReference");
|
||||||
|
final var setup = projectLocalStudioSetupService.load(target);
|
||||||
|
final var state = projectLocalStudioStateService.load(target);
|
||||||
|
final LspProjectContext lspProjectContext = toLspProjectContext(target);
|
||||||
|
final LspServerHandle lspServerHandle = lspServerLifecycle.bootServer(LspServerBootRequest.defaults(lspProjectContext));
|
||||||
return new StudioProjectSession(
|
return new StudioProjectSession(
|
||||||
target,
|
target,
|
||||||
projectLocalStudioStateService,
|
projectLocalStudioStateService,
|
||||||
projectLocalStudioSetupService.load(target),
|
setup,
|
||||||
new StudioExecutionSessionService(),
|
new StudioExecutionSessionService(),
|
||||||
projectLocalStudioStateService.load(target));
|
lspServerLifecycle,
|
||||||
|
lspServerHandle,
|
||||||
|
state);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LspProjectContext toLspProjectContext(final ProjectReference projectReference) {
|
||||||
|
return new LspProjectContext(
|
||||||
|
projectReference.rootPath().toAbsolutePath().normalize().toString(),
|
||||||
|
projectReference.languageId(),
|
||||||
|
projectReference.rootPath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,7 +37,7 @@ public final class StudioWindowCoordinator {
|
|||||||
this.projectCatalogService = new ProjectCatalogService(resolveDefaultProjectsRoot());
|
this.projectCatalogService = new ProjectCatalogService(resolveDefaultProjectsRoot());
|
||||||
this.knownProjectsService = new KnownProjectsService(resolveKnownProjectsStorage(), projectCatalogService);
|
this.knownProjectsService = new KnownProjectsService(resolveKnownProjectsStorage(), projectCatalogService);
|
||||||
this.windowStateService = new WindowStateService(resolveWindowStateStorage());
|
this.windowStateService = new WindowStateService(resolveWindowStateStorage());
|
||||||
this.projectSessionFactory = new StudioProjectSessionFactory();
|
this.projectSessionFactory = new StudioProjectSessionFactory(Container.lspServerLifecycle());
|
||||||
this.launcherView = new ProjectLauncherView(
|
this.launcherView = new ProjectLauncherView(
|
||||||
knownProjectsService,
|
knownProjectsService,
|
||||||
projectCatalogService,
|
projectCatalogService,
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
package p.studio.projectsessions;
|
package p.studio.projectsessions;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import p.studio.lsp.api.LspProjectContext;
|
||||||
|
import p.studio.lsp.api.LspServerBootRequest;
|
||||||
|
import p.studio.lsp.api.LspServerHandle;
|
||||||
|
import p.studio.lsp.api.LspServerLifecycle;
|
||||||
import p.studio.projectstate.ProjectLocalStudioSetup;
|
import p.studio.projectstate.ProjectLocalStudioSetup;
|
||||||
import p.studio.projectstate.ProjectLocalStudioSetupService;
|
import p.studio.projectstate.ProjectLocalStudioSetupService;
|
||||||
import p.studio.projectstate.ProjectLocalStudioState;
|
import p.studio.projectstate.ProjectLocalStudioState;
|
||||||
@ -10,16 +14,20 @@ import p.studio.projects.ProjectReference;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
final class StudioProjectSessionFactoryTest {
|
final class StudioProjectSessionFactoryTest {
|
||||||
@Test
|
@Test
|
||||||
void openCreatesProjectSessionWithoutLegacyEditorServices() {
|
void openCreatesProjectSessionAndBootsLspServer() {
|
||||||
final RecordingProjectLocalStudioStateService stateService = new RecordingProjectLocalStudioStateService();
|
final RecordingProjectLocalStudioStateService stateService = new RecordingProjectLocalStudioStateService();
|
||||||
final RecordingProjectLocalStudioSetupService setupService = new RecordingProjectLocalStudioSetupService();
|
final RecordingProjectLocalStudioSetupService setupService = new RecordingProjectLocalStudioSetupService();
|
||||||
|
final RecordingLspServerLifecycle lspServerLifecycle = new RecordingLspServerLifecycle();
|
||||||
final StudioProjectSessionFactory sessionFactory = new StudioProjectSessionFactory(
|
final StudioProjectSessionFactory sessionFactory = new StudioProjectSessionFactory(
|
||||||
stateService,
|
stateService,
|
||||||
setupService);
|
setupService,
|
||||||
|
lspServerLifecycle);
|
||||||
final ProjectReference projectReference = new ProjectReference(
|
final ProjectReference projectReference = new ProjectReference(
|
||||||
"Example",
|
"Example",
|
||||||
"1.0.0",
|
"1.0.0",
|
||||||
@ -35,6 +43,29 @@ final class StudioProjectSessionFactoryTest {
|
|||||||
assertEquals("IDLE", session.executionSession().snapshot().state().name());
|
assertEquals("IDLE", session.executionSession().snapshot().state().name());
|
||||||
assertSame(projectReference, stateService.loadedProjectReference);
|
assertSame(projectReference, stateService.loadedProjectReference);
|
||||||
assertSame(projectReference, setupService.loadedProjectReference);
|
assertSame(projectReference, setupService.loadedProjectReference);
|
||||||
|
assertNotNull(session.lspServerHandle());
|
||||||
|
assertEquals(1, lspServerLifecycle.bootCalls);
|
||||||
|
assertEquals(projectReference.languageId(), lspServerLifecycle.lastRequest.project().languageId());
|
||||||
|
assertEquals(projectReference.rootPath().toAbsolutePath().normalize(), lspServerLifecycle.lastRequest.project().projectRoot());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void openSurfacesLspBootFailure() {
|
||||||
|
final RecordingProjectLocalStudioStateService stateService = new RecordingProjectLocalStudioStateService();
|
||||||
|
final RecordingProjectLocalStudioSetupService setupService = new RecordingProjectLocalStudioSetupService();
|
||||||
|
final StudioProjectSessionFactory sessionFactory = new StudioProjectSessionFactory(
|
||||||
|
stateService,
|
||||||
|
setupService,
|
||||||
|
new FailingLspServerLifecycle());
|
||||||
|
|
||||||
|
final IllegalStateException failure = assertThrows(IllegalStateException.class, () -> sessionFactory.open(new ProjectReference(
|
||||||
|
"Example",
|
||||||
|
"1.0.0",
|
||||||
|
"pbs",
|
||||||
|
1,
|
||||||
|
Path.of("/tmp/example"))));
|
||||||
|
|
||||||
|
assertEquals("boom", failure.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class RecordingProjectLocalStudioStateService extends ProjectLocalStudioStateService {
|
private static final class RecordingProjectLocalStudioStateService extends ProjectLocalStudioStateService {
|
||||||
@ -63,4 +94,33 @@ final class StudioProjectSessionFactoryTest {
|
|||||||
return loadedSetup;
|
return loadedSetup;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class RecordingLspServerLifecycle implements LspServerLifecycle {
|
||||||
|
private int bootCalls;
|
||||||
|
private LspServerBootRequest lastRequest;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LspServerHandle bootServer(final LspServerBootRequest request) {
|
||||||
|
bootCalls++;
|
||||||
|
lastRequest = request;
|
||||||
|
return new LspServerHandle(
|
||||||
|
request.project(),
|
||||||
|
new p.studio.lsp.api.LspServerEndpoint("127.0.0.1", request.configuration().port()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shutdownServer(final LspProjectContext project) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class FailingLspServerLifecycle implements LspServerLifecycle {
|
||||||
|
@Override
|
||||||
|
public LspServerHandle bootServer(final LspServerBootRequest request) {
|
||||||
|
throw new IllegalStateException("boom");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shutdownServer(final LspProjectContext project) {
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,9 @@ package p.studio.projectsessions;
|
|||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import p.studio.execution.StudioExecutionSessionService;
|
import p.studio.execution.StudioExecutionSessionService;
|
||||||
|
import p.studio.lsp.api.LspProjectContext;
|
||||||
|
import p.studio.lsp.api.LspServerHandle;
|
||||||
|
import p.studio.lsp.api.LspServerLifecycle;
|
||||||
import p.studio.projectstate.ProjectLocalStudioSetup;
|
import p.studio.projectstate.ProjectLocalStudioSetup;
|
||||||
import p.studio.projectstate.ProjectLocalStudioState;
|
import p.studio.projectstate.ProjectLocalStudioState;
|
||||||
import p.studio.projectstate.ProjectLocalStudioStateService;
|
import p.studio.projectstate.ProjectLocalStudioStateService;
|
||||||
@ -21,6 +24,8 @@ final class StudioProjectSessionTest {
|
|||||||
stateService,
|
stateService,
|
||||||
ProjectLocalStudioSetup.defaults(),
|
ProjectLocalStudioSetup.defaults(),
|
||||||
new StudioExecutionSessionService(),
|
new StudioExecutionSessionService(),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
ProjectLocalStudioState.defaults());
|
ProjectLocalStudioState.defaults());
|
||||||
|
|
||||||
session.close();
|
session.close();
|
||||||
@ -38,6 +43,8 @@ final class StudioProjectSessionTest {
|
|||||||
stateService,
|
stateService,
|
||||||
ProjectLocalStudioSetup.defaults(),
|
ProjectLocalStudioSetup.defaults(),
|
||||||
new StudioExecutionSessionService(),
|
new StudioExecutionSessionService(),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
ProjectLocalStudioState.defaults());
|
ProjectLocalStudioState.defaults());
|
||||||
final ProjectLocalStudioState nextState = ProjectLocalStudioState.defaults()
|
final ProjectLocalStudioState nextState = ProjectLocalStudioState.defaults()
|
||||||
.withOpenShellState(new ProjectLocalStudioState.OpenShellState("ASSETS"));
|
.withOpenShellState(new ProjectLocalStudioState.OpenShellState("ASSETS"));
|
||||||
@ -48,6 +55,29 @@ final class StudioProjectSessionTest {
|
|||||||
assertSame(nextState, stateService.savedState);
|
assertSame(nextState, stateService.savedState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void closeShutsDownLspServerOnlyOnce() {
|
||||||
|
final RecordingProjectLocalStudioStateService stateService = new RecordingProjectLocalStudioStateService();
|
||||||
|
final RecordingLspServerLifecycle lspServerLifecycle = new RecordingLspServerLifecycle();
|
||||||
|
final LspServerHandle lspServerHandle = new LspServerHandle(
|
||||||
|
new LspProjectContext("demo", "pbs", Path.of("/tmp/example")),
|
||||||
|
new p.studio.lsp.api.LspServerEndpoint("127.0.0.1", 7777));
|
||||||
|
final StudioProjectSession session = new StudioProjectSession(
|
||||||
|
projectReference(),
|
||||||
|
stateService,
|
||||||
|
ProjectLocalStudioSetup.defaults(),
|
||||||
|
new StudioExecutionSessionService(),
|
||||||
|
lspServerLifecycle,
|
||||||
|
lspServerHandle,
|
||||||
|
ProjectLocalStudioState.defaults());
|
||||||
|
|
||||||
|
session.close();
|
||||||
|
session.close();
|
||||||
|
|
||||||
|
assertEquals(1, lspServerLifecycle.shutdownCalls);
|
||||||
|
assertEquals(lspServerHandle.project(), lspServerLifecycle.lastProject);
|
||||||
|
}
|
||||||
|
|
||||||
private ProjectReference projectReference() {
|
private ProjectReference projectReference() {
|
||||||
return new ProjectReference("Example", "1.0.0", "pbs", 1, Path.of("/tmp/example"));
|
return new ProjectReference("Example", "1.0.0", "pbs", 1, Path.of("/tmp/example"));
|
||||||
}
|
}
|
||||||
@ -64,4 +94,20 @@ final class StudioProjectSessionTest {
|
|||||||
savedState = state;
|
savedState = state;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class RecordingLspServerLifecycle implements LspServerLifecycle {
|
||||||
|
private int shutdownCalls;
|
||||||
|
private LspProjectContext lastProject;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LspServerHandle bootServer(final p.studio.lsp.api.LspServerBootRequest request) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shutdownServer(final LspProjectContext project) {
|
||||||
|
shutdownCalls++;
|
||||||
|
lastProject = project;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user