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
|
# Ignore Kotlin plugin data
|
||||||
.kotlin
|
.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/**
|
**/.studio/**
|
||||||
|
|
||||||
|
|||||||
@ -114,6 +114,15 @@ public enum I18n {
|
|||||||
WORKSPACE_SHIPPER_LOGS("workspace.shipper.logs"),
|
WORKSPACE_SHIPPER_LOGS("workspace.shipper.logs"),
|
||||||
WORKSPACE_SHIPPER_BUTTON_RUN("workspace.shipper.button.run"),
|
WORKSPACE_SHIPPER_BUTTON_RUN("workspace.shipper.button.run"),
|
||||||
WORKSPACE_SHIPPER_BUTTON_CLEAR("workspace.shipper.button.clear"),
|
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"),
|
WORKSPACE_ASSETS("workspace.assets"),
|
||||||
ASSETS_NAVIGATOR_TITLE("assets.navigator.title"),
|
ASSETS_NAVIGATOR_TITLE("assets.navigator.title"),
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import p.studio.workspaces.WorkspaceHost;
|
|||||||
import p.studio.workspaces.WorkspaceId;
|
import p.studio.workspaces.WorkspaceId;
|
||||||
import p.studio.workspaces.assets.AssetWorkspace;
|
import p.studio.workspaces.assets.AssetWorkspace;
|
||||||
import p.studio.workspaces.builder.ShipperWorkspace;
|
import p.studio.workspaces.builder.ShipperWorkspace;
|
||||||
|
import p.studio.workspaces.debug.DebugWorkspace;
|
||||||
import p.studio.workspaces.editor.EditorWorkspace;
|
import p.studio.workspaces.editor.EditorWorkspace;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -49,7 +50,7 @@ public final class MainView extends BorderPane {
|
|||||||
editorWorkspace.restoreProjectLocalState(persistedState);
|
editorWorkspace.restoreProjectLocalState(persistedState);
|
||||||
assetWorkspace.setStateChangedAction(this::persistProjectLocalState);
|
assetWorkspace.setStateChangedAction(this::persistProjectLocalState);
|
||||||
editorWorkspace.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));
|
host.register(new ShipperWorkspace(projectReference));
|
||||||
|
|
||||||
workspaceRail = new StudioWorkspaceRailControl<>(
|
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.logs=Logs
|
||||||
workspace.shipper.button.run=Build
|
workspace.shipper.button.run=Build
|
||||||
workspace.shipper.button.clear=Clear
|
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
|
workspace.assets=Assets
|
||||||
assets.navigator.title=Asset Navigator
|
assets.navigator.title=Asset Navigator
|
||||||
|
|||||||
@ -19,4 +19,13 @@ workspace.shipper.button.run=Construir
|
|||||||
workspace.shipper.button.clear=Limpar
|
workspace.shipper.button.clear=Limpar
|
||||||
|
|
||||||
workspace.assets=Assets
|
workspace.assets=Assets
|
||||||
workspace.debug=Depurar
|
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