implements PLN-0043 debug workspace first wave
This commit is contained in:
parent
56be8fa69e
commit
16503b2575
6
.gitignore
vendored
6
.gitignore
vendored
@ -10,7 +10,11 @@ build
|
||||
# Ignore Kotlin plugin data
|
||||
.kotlin
|
||||
|
||||
debug
|
||||
/debug/
|
||||
!prometeu-studio/src/main/java/p/studio/workspaces/debug/
|
||||
!prometeu-studio/src/main/java/p/studio/workspaces/debug/**
|
||||
!prometeu-studio/src/test/java/p/studio/workspaces/debug/
|
||||
!prometeu-studio/src/test/java/p/studio/workspaces/debug/**
|
||||
|
||||
**/.studio/**
|
||||
|
||||
|
||||
@ -114,6 +114,15 @@ public enum I18n {
|
||||
WORKSPACE_SHIPPER_LOGS("workspace.shipper.logs"),
|
||||
WORKSPACE_SHIPPER_BUTTON_RUN("workspace.shipper.button.run"),
|
||||
WORKSPACE_SHIPPER_BUTTON_CLEAR("workspace.shipper.button.clear"),
|
||||
WORKSPACE_DEBUG_STATE("workspace.debug.state"),
|
||||
WORKSPACE_DEBUG_LOGS("workspace.debug.logs"),
|
||||
WORKSPACE_DEBUG_STATUS_IDLE("workspace.debug.status.idle"),
|
||||
WORKSPACE_DEBUG_STATUS_PREPARING("workspace.debug.status.preparing"),
|
||||
WORKSPACE_DEBUG_STATUS_PREPARE_FAILED("workspace.debug.status.prepareFailed"),
|
||||
WORKSPACE_DEBUG_STATUS_CONNECTING("workspace.debug.status.connecting"),
|
||||
WORKSPACE_DEBUG_STATUS_RUNNING("workspace.debug.status.running"),
|
||||
WORKSPACE_DEBUG_STATUS_RUNTIME_FAILED("workspace.debug.status.runtimeFailed"),
|
||||
WORKSPACE_DEBUG_STATUS_STOPPED("workspace.debug.status.stopped"),
|
||||
|
||||
WORKSPACE_ASSETS("workspace.assets"),
|
||||
ASSETS_NAVIGATOR_TITLE("assets.navigator.title"),
|
||||
|
||||
@ -13,6 +13,7 @@ import p.studio.workspaces.WorkspaceHost;
|
||||
import p.studio.workspaces.WorkspaceId;
|
||||
import p.studio.workspaces.assets.AssetWorkspace;
|
||||
import p.studio.workspaces.builder.ShipperWorkspace;
|
||||
import p.studio.workspaces.debug.DebugWorkspace;
|
||||
import p.studio.workspaces.editor.EditorWorkspace;
|
||||
|
||||
import java.util.List;
|
||||
@ -49,7 +50,7 @@ public final class MainView extends BorderPane {
|
||||
editorWorkspace.restoreProjectLocalState(persistedState);
|
||||
assetWorkspace.setStateChangedAction(this::persistProjectLocalState);
|
||||
editorWorkspace.setStateChangedAction(this::persistProjectLocalState);
|
||||
// host.register(new PlaceholderWorkspace(WorkspaceId.DEBUG, I18n.WORKSPACE_DEBUG, "Debug"));
|
||||
host.register(new DebugWorkspace(projectReference, projectSession.executionSession()));
|
||||
host.register(new ShipperWorkspace(projectReference));
|
||||
|
||||
workspaceRail = new StudioWorkspaceRailControl<>(
|
||||
|
||||
@ -0,0 +1,116 @@
|
||||
package p.studio.workspaces.debug;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.TextArea;
|
||||
import javafx.scene.control.TitledPane;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import p.studio.Container;
|
||||
import p.studio.execution.StudioExecutionSessionService;
|
||||
import p.studio.execution.StudioExecutionSnapshot;
|
||||
import p.studio.execution.StudioExecutionState;
|
||||
import p.studio.projects.ProjectReference;
|
||||
import p.studio.utilities.events.EventSubscription;
|
||||
import p.studio.utilities.i18n.I18n;
|
||||
import p.studio.workspaces.Workspace;
|
||||
import p.studio.workspaces.WorkspaceId;
|
||||
|
||||
public final class DebugWorkspace extends Workspace {
|
||||
private final BorderPane root = new BorderPane();
|
||||
private final StudioExecutionSessionService executionSessionService;
|
||||
private final Label statusValue = new Label();
|
||||
private final TextArea logs = new TextArea();
|
||||
private EventSubscription sessionSubscription;
|
||||
|
||||
public DebugWorkspace(
|
||||
final ProjectReference projectReference,
|
||||
final StudioExecutionSessionService executionSessionService) {
|
||||
super(projectReference);
|
||||
this.executionSessionService = executionSessionService;
|
||||
root.getStyleClass().add("debug-workspace");
|
||||
root.setPadding(new Insets(16));
|
||||
root.setTop(buildStatusPane());
|
||||
root.setCenter(buildLogsPane());
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorkspaceId workspaceId() {
|
||||
return WorkspaceId.DEBUG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public I18n title() {
|
||||
return I18n.WORKSPACE_DEBUG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node rootNode() {
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
if (sessionSubscription != null) {
|
||||
return;
|
||||
}
|
||||
sessionSubscription = executionSessionService.subscribe(this::renderSnapshot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unLoad() {
|
||||
if (sessionSubscription == null) {
|
||||
return;
|
||||
}
|
||||
sessionSubscription.unsubscribe();
|
||||
sessionSubscription = null;
|
||||
}
|
||||
|
||||
private Node buildStatusPane() {
|
||||
final Label title = new Label();
|
||||
title.textProperty().bind(Container.i18n().bind(I18n.WORKSPACE_DEBUG_STATE));
|
||||
title.getStyleClass().add("studio-section-title");
|
||||
statusValue.getStyleClass().add("studio-debug-status-value");
|
||||
return new HBox(12, title, statusValue);
|
||||
}
|
||||
|
||||
private Node buildLogsPane() {
|
||||
logs.setEditable(false);
|
||||
logs.setWrapText(true);
|
||||
logs.setMinHeight(360);
|
||||
final TitledPane pane = new TitledPane();
|
||||
pane.textProperty().bind(Container.i18n().bind(I18n.WORKSPACE_DEBUG_LOGS));
|
||||
pane.setContent(logs);
|
||||
pane.setCollapsible(false);
|
||||
pane.setExpanded(true);
|
||||
return pane;
|
||||
}
|
||||
|
||||
private void renderSnapshot(final StudioExecutionSnapshot snapshot) {
|
||||
if (Platform.isFxApplicationThread()) {
|
||||
applySnapshot(snapshot);
|
||||
return;
|
||||
}
|
||||
Platform.runLater(() -> applySnapshot(snapshot));
|
||||
}
|
||||
|
||||
private void applySnapshot(final StudioExecutionSnapshot snapshot) {
|
||||
final DebugWorkspaceProjection projection = DebugWorkspaceProjection.from(snapshot);
|
||||
statusValue.setText(Container.i18n().text(statusText(projection.state())));
|
||||
logs.setText(String.join(System.lineSeparator(), projection.renderedLogs()));
|
||||
}
|
||||
|
||||
private I18n statusText(final StudioExecutionState state) {
|
||||
return switch (state) {
|
||||
case IDLE -> I18n.WORKSPACE_DEBUG_STATUS_IDLE;
|
||||
case PREPARING -> I18n.WORKSPACE_DEBUG_STATUS_PREPARING;
|
||||
case PREPARE_FAILED -> I18n.WORKSPACE_DEBUG_STATUS_PREPARE_FAILED;
|
||||
case CONNECTING -> I18n.WORKSPACE_DEBUG_STATUS_CONNECTING;
|
||||
case RUNNING -> I18n.WORKSPACE_DEBUG_STATUS_RUNNING;
|
||||
case RUNTIME_FAILED -> I18n.WORKSPACE_DEBUG_STATUS_RUNTIME_FAILED;
|
||||
case STOPPED -> I18n.WORKSPACE_DEBUG_STATUS_STOPPED;
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package p.studio.workspaces.debug;
|
||||
|
||||
import p.studio.execution.StudioExecutionLogEntry;
|
||||
import p.studio.execution.StudioExecutionSnapshot;
|
||||
import p.studio.execution.StudioExecutionState;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public record DebugWorkspaceProjection(
|
||||
StudioExecutionState state,
|
||||
List<String> renderedLogs) {
|
||||
|
||||
public DebugWorkspaceProjection {
|
||||
Objects.requireNonNull(state, "state");
|
||||
renderedLogs = List.copyOf(Objects.requireNonNull(renderedLogs, "renderedLogs"));
|
||||
}
|
||||
|
||||
public static DebugWorkspaceProjection from(final StudioExecutionSnapshot snapshot) {
|
||||
final StudioExecutionSnapshot safeSnapshot = Objects.requireNonNull(snapshot, "snapshot");
|
||||
return new DebugWorkspaceProjection(
|
||||
safeSnapshot.state(),
|
||||
safeSnapshot.logs().stream().map(DebugWorkspaceProjection::renderLog).toList());
|
||||
}
|
||||
|
||||
private static String renderLog(final StudioExecutionLogEntry entry) {
|
||||
return "[%s][%s] %s".formatted(
|
||||
entry.source().name(),
|
||||
entry.severity().name(),
|
||||
entry.message());
|
||||
}
|
||||
}
|
||||
@ -104,6 +104,15 @@ workspace.shipper=Shipper
|
||||
workspace.shipper.logs=Logs
|
||||
workspace.shipper.button.run=Build
|
||||
workspace.shipper.button.clear=Clear
|
||||
workspace.debug.state=State
|
||||
workspace.debug.logs=Execution Logs
|
||||
workspace.debug.status.idle=Idle
|
||||
workspace.debug.status.preparing=Preparing
|
||||
workspace.debug.status.prepareFailed=Preparation failed
|
||||
workspace.debug.status.connecting=Connecting
|
||||
workspace.debug.status.running=Running
|
||||
workspace.debug.status.runtimeFailed=Runtime failed
|
||||
workspace.debug.status.stopped=Stopped
|
||||
|
||||
workspace.assets=Assets
|
||||
assets.navigator.title=Asset Navigator
|
||||
|
||||
@ -20,3 +20,12 @@ workspace.shipper.button.clear=Limpar
|
||||
|
||||
workspace.assets=Assets
|
||||
workspace.debug=Depurar
|
||||
workspace.debug.state=Estado
|
||||
workspace.debug.logs=Logs de Execucao
|
||||
workspace.debug.status.idle=Idle
|
||||
workspace.debug.status.preparing=Preparando
|
||||
workspace.debug.status.prepareFailed=Preparacao falhou
|
||||
workspace.debug.status.connecting=Conectando
|
||||
workspace.debug.status.running=Executando
|
||||
workspace.debug.status.runtimeFailed=Runtime falhou
|
||||
workspace.debug.status.stopped=Parado
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
package p.studio.workspaces.debug;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import p.studio.execution.StudioExecutionLogEntry;
|
||||
import p.studio.execution.StudioExecutionLogSeverity;
|
||||
import p.studio.execution.StudioExecutionLogSource;
|
||||
import p.studio.execution.StudioExecutionSnapshot;
|
||||
import p.studio.execution.StudioExecutionState;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
final class DebugWorkspaceProjectionTest {
|
||||
@Test
|
||||
void preservesSessionStateAndVisibleLogOrigins() {
|
||||
final StudioExecutionSnapshot snapshot = new StudioExecutionSnapshot(
|
||||
StudioExecutionState.PREPARING,
|
||||
List.of(
|
||||
new StudioExecutionLogEntry(
|
||||
1L,
|
||||
Instant.parse("2026-04-06T10:00:00Z"),
|
||||
StudioExecutionLogSource.BUILD,
|
||||
StudioExecutionLogSeverity.INFO,
|
||||
"Compiler started"),
|
||||
new StudioExecutionLogEntry(
|
||||
2L,
|
||||
Instant.parse("2026-04-06T10:00:01Z"),
|
||||
StudioExecutionLogSource.PACK,
|
||||
StudioExecutionLogSeverity.ERROR,
|
||||
"Pack failed")));
|
||||
|
||||
final DebugWorkspaceProjection projection = DebugWorkspaceProjection.from(snapshot);
|
||||
|
||||
assertEquals(StudioExecutionState.PREPARING, projection.state());
|
||||
assertEquals(2, projection.renderedLogs().size());
|
||||
assertTrue(projection.renderedLogs().getFirst().contains("[BUILD][INFO]"));
|
||||
assertTrue(projection.renderedLogs().getLast().contains("[PACK][ERROR]"));
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user