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":"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-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"}]}
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
id: PLN-0066
|
||||
ticket: studio-new-lsp-api-and-v1-boundary
|
||||
title: Project-Scoped LSP Server Lifecycle in Studio
|
||||
status: open
|
||||
status: done
|
||||
created: 2026-05-05
|
||||
completed:
|
||||
completed: 2026-05-05
|
||||
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
|
||||
|
||||
- [ ] Opening a Studio project boots an LSP server for that project.
|
||||
- [ ] Closing the project shuts down that server.
|
||||
- [ ] The lifecycle is owned by project session boundaries, not global app startup.
|
||||
- [ ] Existing non-LSP project flows remain operational.
|
||||
- [ ] The current dumb connectivity workflow remains usable as a temporary implementation phase.
|
||||
- [x] Opening a Studio project boots an LSP server for that project.
|
||||
- [x] Closing the project shuts down that server.
|
||||
- [x] The lifecycle is owned by project session boundaries, not global app startup.
|
||||
- [x] Existing non-LSP project flows remain operational.
|
||||
- [x] The current dumb connectivity workflow remains usable as a temporary implementation phase.
|
||||
|
||||
## Dependencies
|
||||
|
||||
|
||||
@ -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-packer:prometeu-packer-api"))
|
||||
implementation(project(":prometeu-packer:prometeu-packer-v1"))
|
||||
implementation(project(":prometeu-studio"))
|
||||
|
||||
@ -2,8 +2,12 @@ package p.studio;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import p.packer.Packer;
|
||||
import p.studio.lsp.api.LspServerLifecycle;
|
||||
import p.studio.lsp.events.StudioEventBus;
|
||||
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.i18n.I18nService;
|
||||
|
||||
@ -19,6 +23,7 @@ public final class AppContainer implements Container {
|
||||
private final ObjectMapper mapper;
|
||||
private final EmbeddedPacker embeddedPacker;
|
||||
private final StudioBackgroundTasks backgroundTasks;
|
||||
private final LspServerLifecycle lspServerLifecycle;
|
||||
|
||||
public AppContainer() {
|
||||
this.i18nService = new I18nService();
|
||||
@ -29,6 +34,9 @@ public final class AppContainer implements Container {
|
||||
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 LspV1ServerLifecycle(
|
||||
new TcpLspServerHostFactory(),
|
||||
new CompilerLanguageServiceBridge());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -61,6 +69,11 @@ public final class AppContainer implements Container {
|
||||
return backgroundTasks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LspServerLifecycle getLspServerLifecycle() {
|
||||
return lspServerLifecycle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdownContainer() {
|
||||
embeddedPacker.shutdown();
|
||||
|
||||
@ -5,6 +5,7 @@ plugins {
|
||||
|
||||
dependencies {
|
||||
implementation(project(":prometeu-infra"))
|
||||
implementation(project(":prometeu-lsp:prometeu-lsp-api"))
|
||||
implementation(project(":prometeu-packer:prometeu-packer-api"))
|
||||
implementation(project(":prometeu-compiler:prometeu-compiler-core"))
|
||||
implementation(project(":prometeu-compiler:prometeu-build-pipeline"))
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package p.studio;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import p.studio.lsp.api.LspServerLifecycle;
|
||||
import p.studio.lsp.events.StudioEventBus;
|
||||
import p.studio.utilities.ThemeService;
|
||||
import p.studio.utilities.i18n.I18nService;
|
||||
@ -21,6 +22,8 @@ public interface Container {
|
||||
|
||||
StudioBackgroundTasks getBackgroundTasks();
|
||||
|
||||
LspServerLifecycle getLspServerLifecycle();
|
||||
|
||||
void shutdownContainer();
|
||||
|
||||
static void install(Container container) {
|
||||
@ -68,6 +71,10 @@ public interface Container {
|
||||
return current().getBackgroundTasks();
|
||||
}
|
||||
|
||||
static LspServerLifecycle lspServerLifecycle() {
|
||||
return current().getLspServerLifecycle();
|
||||
}
|
||||
|
||||
static void shutdown() {
|
||||
final Container container = Holder.CURRENT.get();
|
||||
if (container != null) {
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
package p.studio.projectsessions;
|
||||
|
||||
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.ProjectLocalStudioState;
|
||||
import p.studio.projectstate.ProjectLocalStudioStateService;
|
||||
@ -13,6 +16,8 @@ public final class StudioProjectSession implements AutoCloseable {
|
||||
private final ProjectLocalStudioStateService projectLocalStudioStateService;
|
||||
private final ProjectLocalStudioSetup projectLocalStudioSetup;
|
||||
private final StudioExecutionSessionService executionSessionService;
|
||||
private final LspServerLifecycle lspServerLifecycle;
|
||||
private final LspServerHandle lspServerHandle;
|
||||
private ProjectLocalStudioState projectLocalStudioState;
|
||||
private boolean closed;
|
||||
|
||||
@ -23,6 +28,8 @@ public final class StudioProjectSession implements AutoCloseable {
|
||||
new ProjectLocalStudioStateService(),
|
||||
ProjectLocalStudioSetup.defaults(),
|
||||
new StudioExecutionSessionService(),
|
||||
null,
|
||||
null,
|
||||
ProjectLocalStudioState.defaults());
|
||||
}
|
||||
|
||||
@ -31,11 +38,15 @@ public final class StudioProjectSession implements AutoCloseable {
|
||||
final ProjectLocalStudioStateService projectLocalStudioStateService,
|
||||
final ProjectLocalStudioSetup projectLocalStudioSetup,
|
||||
final StudioExecutionSessionService executionSessionService,
|
||||
final LspServerLifecycle lspServerLifecycle,
|
||||
final LspServerHandle lspServerHandle,
|
||||
final ProjectLocalStudioState projectLocalStudioState) {
|
||||
this.projectReference = Objects.requireNonNull(projectReference, "projectReference");
|
||||
this.projectLocalStudioStateService = Objects.requireNonNull(projectLocalStudioStateService, "projectLocalStudioStateService");
|
||||
this.projectLocalStudioSetup = Objects.requireNonNull(projectLocalStudioSetup, "projectLocalStudioSetup");
|
||||
this.executionSessionService = Objects.requireNonNull(executionSessionService, "executionSessionService");
|
||||
this.lspServerLifecycle = lspServerLifecycle;
|
||||
this.lspServerHandle = lspServerHandle;
|
||||
this.projectLocalStudioState = Objects.requireNonNull(projectLocalStudioState, "projectLocalStudioState");
|
||||
}
|
||||
|
||||
@ -55,6 +66,10 @@ public final class StudioProjectSession implements AutoCloseable {
|
||||
return executionSessionService;
|
||||
}
|
||||
|
||||
public LspServerHandle lspServerHandle() {
|
||||
return lspServerHandle;
|
||||
}
|
||||
|
||||
public void replaceProjectLocalStudioState(final ProjectLocalStudioState nextProjectLocalStudioState) {
|
||||
this.projectLocalStudioState = Objects.requireNonNull(nextProjectLocalStudioState, "nextProjectLocalStudioState");
|
||||
}
|
||||
@ -76,8 +91,12 @@ public final class StudioProjectSession implements AutoCloseable {
|
||||
failure = runtimeException;
|
||||
}
|
||||
try {
|
||||
if (failure != null) {
|
||||
throw failure;
|
||||
shutdownLspServer();
|
||||
} catch (RuntimeException runtimeException) {
|
||||
if (failure == null) {
|
||||
failure = runtimeException;
|
||||
} else {
|
||||
failure.addSuppressed(runtimeException);
|
||||
}
|
||||
} finally {
|
||||
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;
|
||||
|
||||
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.projectstate.ProjectLocalStudioSetupService;
|
||||
import p.studio.projectstate.ProjectLocalStudioStateService;
|
||||
@ -10,27 +14,44 @@ import java.util.Objects;
|
||||
public final class StudioProjectSessionFactory {
|
||||
private final ProjectLocalStudioStateService projectLocalStudioStateService;
|
||||
private final ProjectLocalStudioSetupService projectLocalStudioSetupService;
|
||||
private final LspServerLifecycle lspServerLifecycle;
|
||||
|
||||
public StudioProjectSessionFactory() {
|
||||
public StudioProjectSessionFactory(final LspServerLifecycle lspServerLifecycle) {
|
||||
this(
|
||||
new ProjectLocalStudioStateService(),
|
||||
new ProjectLocalStudioSetupService());
|
||||
new ProjectLocalStudioSetupService(),
|
||||
Objects.requireNonNull(lspServerLifecycle, "lspServerLifecycle"));
|
||||
}
|
||||
|
||||
StudioProjectSessionFactory(
|
||||
final ProjectLocalStudioStateService projectLocalStudioStateService,
|
||||
final ProjectLocalStudioSetupService projectLocalStudioSetupService) {
|
||||
final ProjectLocalStudioSetupService projectLocalStudioSetupService,
|
||||
final LspServerLifecycle lspServerLifecycle) {
|
||||
this.projectLocalStudioStateService = Objects.requireNonNull(projectLocalStudioStateService, "projectLocalStudioStateService");
|
||||
this.projectLocalStudioSetupService = Objects.requireNonNull(projectLocalStudioSetupService, "projectLocalStudioSetupService");
|
||||
this.lspServerLifecycle = Objects.requireNonNull(lspServerLifecycle, "lspServerLifecycle");
|
||||
}
|
||||
|
||||
public StudioProjectSession open(final 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(
|
||||
target,
|
||||
projectLocalStudioStateService,
|
||||
projectLocalStudioSetupService.load(target),
|
||||
setup,
|
||||
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.knownProjectsService = new KnownProjectsService(resolveKnownProjectsStorage(), projectCatalogService);
|
||||
this.windowStateService = new WindowStateService(resolveWindowStateStorage());
|
||||
this.projectSessionFactory = new StudioProjectSessionFactory();
|
||||
this.projectSessionFactory = new StudioProjectSessionFactory(Container.lspServerLifecycle());
|
||||
this.launcherView = new ProjectLauncherView(
|
||||
knownProjectsService,
|
||||
projectCatalogService,
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
package p.studio.projectsessions;
|
||||
|
||||
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.ProjectLocalStudioSetupService;
|
||||
import p.studio.projectstate.ProjectLocalStudioState;
|
||||
@ -10,16 +14,20 @@ import p.studio.projects.ProjectReference;
|
||||
import java.nio.file.Path;
|
||||
|
||||
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.assertThrows;
|
||||
|
||||
final class StudioProjectSessionFactoryTest {
|
||||
@Test
|
||||
void openCreatesProjectSessionWithoutLegacyEditorServices() {
|
||||
void openCreatesProjectSessionAndBootsLspServer() {
|
||||
final RecordingProjectLocalStudioStateService stateService = new RecordingProjectLocalStudioStateService();
|
||||
final RecordingProjectLocalStudioSetupService setupService = new RecordingProjectLocalStudioSetupService();
|
||||
final RecordingLspServerLifecycle lspServerLifecycle = new RecordingLspServerLifecycle();
|
||||
final StudioProjectSessionFactory sessionFactory = new StudioProjectSessionFactory(
|
||||
stateService,
|
||||
setupService);
|
||||
setupService,
|
||||
lspServerLifecycle);
|
||||
final ProjectReference projectReference = new ProjectReference(
|
||||
"Example",
|
||||
"1.0.0",
|
||||
@ -35,6 +43,29 @@ final class StudioProjectSessionFactoryTest {
|
||||
assertEquals("IDLE", session.executionSession().snapshot().state().name());
|
||||
assertSame(projectReference, stateService.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 {
|
||||
@ -63,4 +94,33 @@ final class StudioProjectSessionFactoryTest {
|
||||
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 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.ProjectLocalStudioState;
|
||||
import p.studio.projectstate.ProjectLocalStudioStateService;
|
||||
@ -21,6 +24,8 @@ final class StudioProjectSessionTest {
|
||||
stateService,
|
||||
ProjectLocalStudioSetup.defaults(),
|
||||
new StudioExecutionSessionService(),
|
||||
null,
|
||||
null,
|
||||
ProjectLocalStudioState.defaults());
|
||||
|
||||
session.close();
|
||||
@ -38,6 +43,8 @@ final class StudioProjectSessionTest {
|
||||
stateService,
|
||||
ProjectLocalStudioSetup.defaults(),
|
||||
new StudioExecutionSessionService(),
|
||||
null,
|
||||
null,
|
||||
ProjectLocalStudioState.defaults());
|
||||
final ProjectLocalStudioState nextState = ProjectLocalStudioState.defaults()
|
||||
.withOpenShellState(new ProjectLocalStudioState.OpenShellState("ASSETS"));
|
||||
@ -48,6 +55,29 @@ final class StudioProjectSessionTest {
|
||||
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() {
|
||||
return new ProjectReference("Example", "1.0.0", "pbs", 1, Path.of("/tmp/example"));
|
||||
}
|
||||
@ -64,4 +94,20 @@ final class StudioProjectSessionTest {
|
||||
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