diff --git a/companion/README.md b/companion/README.md index 1b37c01c..c9ff9bbc 100644 --- a/companion/README.md +++ b/companion/README.md @@ -56,3 +56,5 @@ The scope publishes one `RuntimeBinding` for the installed inventory. The bindin The index loader retains ownership while a candidate is prepared. The application detaches the previous runtime, attaches the new compiler/insight bindings, and completes publication under the existing lifecycle lock. Debugger and UI follow-up runs afterward and cannot return an installed index to the loader's failure cleanup. Closing a runtime detaches its consumers before releasing the index, and is idempotent. A rejected candidate closes its own prepared consumers while leaving index disposal to the loader. The script compiler and code-insight worker remain application-lived. Open local editors retain the code-insight service, so a runtime changes its binding rather than replacing that service instance. MCP tool declarations identify project-bound requests. Mutations use the captured scope's atomic admission gate; late results check that scope, and navigation also checks runtime identity. Instance state is flushed before retirement and detach. Stateless JDT parsing is in `JavaAst`; editor analysis/listeners remain in `ASTCache`. + +Java editors receive an `EditorContext` containing their project, runtime-facing services, navigation and window callbacks. Search and settings windows receive their own specific collaborators. `ScriptExecutionService` shares authenticated execution and cancellation between UI and MCP; execution-result subscriptions belong to `CompanionSession` and are removed by their UI/job owners. Window disposal and project switching share the same auxiliary-window cleanup. diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/CompanionApp.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/CompanionApp.java index 17a95c34..7d91b013 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/CompanionApp.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/CompanionApp.java @@ -21,13 +21,12 @@ import com.github.minecraft_ta.totalDebugCompanion.debugger.DebuggerSessionController; import com.github.minecraft_ta.totalDebugCompanion.mcp.CodeModeJobService; import com.github.minecraft_ta.totalDebugCompanion.script.ScriptCompilationService; +import com.github.minecraft_ta.totalDebugCompanion.script.ScriptExecutionService; +import com.github.minecraft_ta.totaldebug.protocol.scnet.OpenClassMessage; import com.github.minecraft_ta.totalDebugCompanion.script.ScriptCompilationService.CompilationResult; -import com.github.minecraft_ta.totaldebug.protocol.execution.ExecutionResult; -import com.github.minecraft_ta.totaldebug.protocol.execution.ScriptExecutionEnvironment; import com.github.minecraft_ta.totaldebug.protocol.scnet.StopScriptMessage; import com.github.minecraft_ta.totalDebugCompanion.mcp.CompanionMcpServer; import com.github.minecraft_ta.totaldebug.protocol.scnet.DebugTargetMessage; -import com.github.minecraft_ta.totaldebug.protocol.scnet.RetryRuntimeInventoryMessage; import com.github.minecraft_ta.totaldebug.protocol.scnet.RuntimeInventoryMessage; import com.github.minecraft_ta.totaldebug.protocol.scnet.ServerManifestMessage; import com.github.minecraft_ta.totalDebugCompanion.model.ServiceStatus; @@ -51,7 +50,6 @@ import com.github.minecraft_ta.totalDebugCompanion.ui.theme.ThemeManager; import com.github.minecraft_ta.totalDebugCompanion.ui.views.MainWindow; import com.github.minecraft_ta.totalDebugCompanion.util.UIUtils; -import com.github.tth05.scnet.Server; import com.github.tth05.scnet.message.AbstractMessage; import com.github.tth05.jindex.ClassIndex; import org.eclipse.jdt.core.dom.ASTParser; @@ -82,13 +80,12 @@ import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; -import java.util.function.Consumer; public final class CompanionApp { private static final SecureRandom TOKEN_RANDOM = new SecureRandom(); private static final CountDownLatch EXIT = new CountDownLatch(1); - public static Server SERVER; + private static ScriptExecutionService scriptExecutions; private static CompanionSession session; private static CompanionLaunchConfiguration launchConfiguration; private static final Object lifecycleLock = new Object(); @@ -186,6 +183,10 @@ static int run(String[] args, Map environment, CompanionTimeouts restoreProfile(); session = new CompanionSession(token, CompanionApp::attachSelectedProfile, new CompanionSession.Listener() { + @Override public void openClass(OpenClassMessage message) { + CompanionApp.openClass(message.binaryName(), message.targetType(), message.targetIdentifier()); + } + @Override public void focusWindow() { CompanionApp.focusWindow(); } @Override public void connecting() { updateGameStatus(new ServiceStatus( @@ -234,7 +235,7 @@ public void debugTarget(DebugTargetMessage message) { handleDebugTarget(message); } }); - SERVER = session.server(); + scriptExecutions = new ScriptExecutionService(session, scriptCompiler, CompanionApp::isConnected); session.setProjectSelectionHandler(hello -> { try { openProject(CompanionProfile.fromHello(hello)).join(); } catch (java.util.concurrent.CompletionException failure) { @@ -563,6 +564,9 @@ private static void finishRuntimeInstallation(RuntimeBinding installed, ProjectS } static void configureWithoutSession(CompanionProfile developmentProfile) { + session = new CompanionSession("ui-development"); + scriptExecutions = new ScriptExecutionService(session, scriptCompiler, CompanionApp::isConnected); + runtimeIndexService = new RuntimeIndexService(lifecycleLock, CompanionApp::installRuntimeSnapshot); debuggerController = createDebuggerController(); try { activateProfile(Objects.requireNonNull(developmentProfile, "developmentProfile")); @@ -667,7 +671,7 @@ private static void prewarmJavaParser() { private static void startMcpServer() throws Exception { CodeModeJobService jobs = new CodeModeJobService( - SERVER, + session, scriptExecutions, CompanionApp::requireProject, () -> isConnected(), CompanionApp::runtimeContext ); @@ -735,13 +739,18 @@ private static Map runtimeContext() { return Map.copyOf(context); } + public static MainWindow createMainWindow() { + return new MainWindow(CompanionApp::currentScope, getDebuggerController(), codeInsightService, + scriptExecutions, session, runtimeIndexService, CompanionApp::openDebugFrame, CompanionApp::exit); + } + private static void startUi() throws InvocationTargetException, InterruptedException { SwingUtilities.invokeAndWait(() -> { uiStarted = true; MainWindow.INSTANCE.setSize(1280, 720); MainWindow.INSTANCE.setRuntimeIndexStatus(getRuntimeIndexStatus()); MainWindow.INSTANCE.setVisible(true); - UIUtils.centerJFrame(MainWindow.INSTANCE); + UIUtils.centerJFrame(MainWindow.INSTANCE, MainWindow.INSTANCE); ToolTipManager.sharedInstance().setInitialDelay(200); }); } @@ -863,21 +872,8 @@ public static boolean isCurrentRuntimeInventory(String inventoryId) { return isConnected() && scriptCompiler.isCurrentInventory(inventoryId); } - public static boolean runScript(int id, String source, boolean serverSide, - ScriptExecutionEnvironment environment, Consumer failureHandler) { - synchronized (lifecycleLock) { - ProjectScope scope = current; - if (scope == null || !scope.isActive() || !isConnected()) return false; - return scope.admit(() -> { - scriptCompiler.submit(id, source, serverSide, environment, failureHandler); - return true; - }); - } - } - - public static boolean stopScript(int id) { - return scriptCompiler.cancel(id) || send(new StopScriptMessage(id)); - } + public static ScriptExecutionService scriptExecutions() { return scriptExecutions; } + public static CompanionSession session() { return session; } public static void openClass(String binaryName, int targetType, String targetIdentifier) { openOrQueue( @@ -1021,28 +1017,6 @@ public static RuntimeIndexService.Status getRuntimeIndexStatus() { : service.status(); } - public static void addRuntimeIndexStatusListener(Consumer listener) { - RuntimeIndexService service = runtimeIndexService; - if (service != null) { - service.addStatusListener(listener); - } else { - listener.accept(getRuntimeIndexStatus()); - } - } - - public static void removeRuntimeIndexStatusListener(Consumer listener) { - RuntimeIndexService service = runtimeIndexService; - if (service != null) service.removeStatusListener(listener); - } - - public static void retryRuntimeIndex() { - RuntimeIndexService service = runtimeIndexService; - if (service != null) { - service.waiting("Requesting runtime inventory again"); - } - send(new RetryRuntimeInventoryMessage()); - } - private static CompanionProfile requireProfile() { CompanionProfile current = currentProject(); if (current == null) { diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/jdt/diagnostics/CustomJavaLinkGenerator.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/jdt/diagnostics/CustomJavaLinkGenerator.java index 3395c317..4a62e2dc 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/jdt/diagnostics/CustomJavaLinkGenerator.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/jdt/diagnostics/CustomJavaLinkGenerator.java @@ -3,7 +3,6 @@ import com.github.minecraft_ta.totalDebugCompanion.jdt.symbol.JavaSymbolResolver; import com.github.minecraft_ta.totalDebugCompanion.navigation.NavigationTarget; import com.github.minecraft_ta.totalDebugCompanion.navigation.RuntimeMember; -import com.github.minecraft_ta.totalDebugCompanion.ui.views.MainWindow; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.JavaModelException; @@ -35,13 +34,13 @@ interface ElementResolver { private final Consumer navigator; private final Path sourcePath; - public CustomJavaLinkGenerator(String identifier) { + public CustomJavaLinkGenerator(String identifier, BiConsumer packageNavigator, Consumer navigator) { this( offset -> JavaSymbolResolver.selectElement(identifier, offset), offset -> JavaSymbolResolver.navigationOwnerClass(identifier, offset), - MainWindow.INSTANCE::revealPackage, + packageNavigator, Path.of(identifier), - target -> MainWindow.INSTANCE.navigation().navigate(target) + navigator ); } diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/mcp/CodeModeJobService.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/mcp/CodeModeJobService.java index 6d0102bb..841a2c96 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/mcp/CodeModeJobService.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/mcp/CodeModeJobService.java @@ -2,11 +2,12 @@ import com.github.minecraft_ta.totaldebug.protocol.execution.ScriptExecutionEnvironment; import com.github.minecraft_ta.totalDebugCompanion.script.ExecutionValuePresentation; -import com.github.minecraft_ta.totalDebugCompanion.CompanionApp; +import com.github.minecraft_ta.totalDebugCompanion.project.ProjectScope; +import com.github.minecraft_ta.totalDebugCompanion.script.ScriptExecutionService; +import com.github.minecraft_ta.totalDebugCompanion.session.CompanionSession; import com.github.minecraft_ta.totaldebug.protocol.scnet.ExecutionResultMessage; import com.github.minecraft_ta.totaldebug.protocol.execution.ExecutionResult; import com.github.minecraft_ta.totaldebug.protocol.execution.ExecutionText; -import com.github.tth05.scnet.Server; import java.time.Clock; import java.time.Instant; import java.util.ArrayList; @@ -31,7 +32,8 @@ public final class CodeModeJobService implements AutoCloseable { static final int MAX_RETAINED_JOBS = 256; static final int MAX_WAIT_MILLISECONDS = 120_000; - private final Server server; + private final CompanionSession session; + private Consumer resultListener; private final ExecutorService statusExecutor; private final BooleanSupplier available; private final Transport transport; @@ -43,12 +45,14 @@ public final class CodeModeJobService implements AutoCloseable { private volatile boolean closed; public CodeModeJobService( - Server server, + CompanionSession session, + ScriptExecutionService scripts, + Supplier project, BooleanSupplier available, Supplier> runtimeContext ) { this( - Objects.requireNonNull(server, "server"), + Objects.requireNonNull(session, "session"), Executors.newSingleThreadExecutor(runnable -> { Thread thread = new Thread(runnable, "Companion code-mode status"); thread.setDaemon(true); @@ -64,7 +68,8 @@ public void execute( ExecutionEnvironment environment, Consumer failureHandler ) { - boolean sent = CompanionApp.runScript( + boolean sent = scripts.run( + project.get(), scriptId, source, side == ExecutionSide.SERVER, @@ -78,7 +83,7 @@ public void execute( @Override public void cancel(int scriptId) { - if (!CompanionApp.stopScript(scriptId)) { + if (!scripts.stop(scriptId)) { throw new IllegalStateException("Minecraft disconnected before cancellation was sent"); } } @@ -86,14 +91,15 @@ public void cancel(int scriptId) { runtimeContext, Clock.systemUTC() ); - server.getMessageBus().listenAlways(ExecutionResultMessage.class, this, message -> { + this.resultListener = message -> { int scriptId = message.scriptId(); ExecutionResult result = message.result(); try { this.statusExecutor.execute(() -> acceptResult(scriptId, result)); } catch (RejectedExecutionException ignored) { } - }); + }; + session.addExecutionResultListener(this.resultListener); } CodeModeJobService( @@ -121,14 +127,14 @@ public void cancel(int scriptId) { } private CodeModeJobService( - Server server, + CompanionSession session, ExecutorService statusExecutor, BooleanSupplier available, Transport transport, Supplier> runtimeContext, Clock clock ) { - this.server = server; + this.session = session; this.statusExecutor = statusExecutor; this.available = Objects.requireNonNull(available, "available"); this.transport = Objects.requireNonNull(transport, "transport"); @@ -355,8 +361,8 @@ public void close() { return; } this.closed = true; - if (this.server != null) { - this.server.getMessageBus().unregister(ExecutionResultMessage.class, this); + if (this.session != null) { + this.session.removeExecutionResultListener(this.resultListener); } if (this.statusExecutor != null) { this.statusExecutor.shutdownNow(); diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/model/CodeView.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/model/CodeView.java index 93c9c3b6..a0b5db59 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/model/CodeView.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/model/CodeView.java @@ -1,8 +1,8 @@ package com.github.minecraft_ta.totalDebugCompanion.model; import com.github.minecraft_ta.totalDebugCompanion.runtime.RuntimeBinding; +import com.github.minecraft_ta.totalDebugCompanion.ui.EditorContext; import com.formdev.flatlaf.util.StringUtils; -import com.github.minecraft_ta.totalDebugCompanion.CompanionApp; import com.github.minecraft_ta.totalDebugCompanion.Icons; import com.github.minecraft_ta.totalDebugCompanion.decompile.DecompiledSource; import com.github.minecraft_ta.totalDebugCompanion.debugger.DebugEngine; @@ -31,27 +31,27 @@ public class CodeView implements IEditorPanel { private final CodeViewPanel codeViewPanel; private volatile CompletableFuture ready = CompletableFuture.completedFuture(null); - public CodeView(Path path, int offset) { - this(path, offset, EditorLocation.forFile(path, CompanionApp.getWorkspaceDirectory())); + public CodeView(EditorContext context, Path path, int offset) { + this(context, path, offset, EditorLocation.forFile(path, context.project().profile().workspaceDirectory())); } - public CodeView(Path path, int offset, EditorLocation location) { + public CodeView(EditorContext context, Path path, int offset, EditorLocation location) { this.runtimeBinding = null; this.path = path; this.location = location; this.debugSource = null; this.navigationTarget = new NavigationTarget.LocalFile(path); - this.codeViewPanel = new CodeViewPanel(this); + this.codeViewPanel = new CodeViewPanel(context, this); reload(offset); } - public CodeView(DecompiledSource source, int offset, EditorLocation location, RuntimeBinding runtimeBinding) { + public CodeView(EditorContext context, DecompiledSource source, int offset, EditorLocation location, RuntimeBinding runtimeBinding) { this.runtimeBinding = runtimeBinding; this.path = source.path(); this.location = location; this.debugSource = source.debugSource(); this.navigationTarget = new NavigationTarget.RuntimeClass(source.binaryName()); - this.codeViewPanel = new CodeViewPanel(this); + this.codeViewPanel = new CodeViewPanel(context, this); setCode(source.contents(), offset); } diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/model/LiteralUsagesView.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/model/LiteralUsagesView.java index e2d1f078..5fab44ba 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/model/LiteralUsagesView.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/model/LiteralUsagesView.java @@ -1,7 +1,7 @@ package com.github.minecraft_ta.totalDebugCompanion.model; import com.github.minecraft_ta.totalDebugCompanion.runtime.RuntimeBinding; -import com.github.minecraft_ta.totalDebugCompanion.CompanionApp; +import com.github.minecraft_ta.totalDebugCompanion.ui.EditorContext; import com.github.minecraft_ta.totalDebugCompanion.Icons; import com.github.minecraft_ta.totalDebugCompanion.bytecode.reference.ReferenceQuery; import com.github.minecraft_ta.totalDebugCompanion.navigation.NavigationTarget; @@ -18,7 +18,7 @@ public final class LiteralUsagesView implements IEditorPanel { private final String literal; private final UsagesViewPanel panel; - public LiteralUsagesView(String literal, RuntimeBinding runtimeBinding) { + public LiteralUsagesView(EditorContext context, String literal, RuntimeBinding runtimeBinding) { if (runtimeBinding == null) throw new IllegalStateException("Reference search is unavailable"); this.runtimeBinding = runtimeBinding; this.literal = Objects.requireNonNull(literal, "literal"); @@ -26,7 +26,7 @@ public LiteralUsagesView(String literal, RuntimeBinding runtimeBinding) { ReferenceQuery.stringLiteral(literal), quotedPreview(literal), Icons.VALUE, - runtimeBinding.references() + runtimeBinding.references(), context.navigation()::navigate ); } diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/model/ResourceView.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/model/ResourceView.java index 6bcd8dfe..5db34947 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/model/ResourceView.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/model/ResourceView.java @@ -1,10 +1,10 @@ package com.github.minecraft_ta.totalDebugCompanion.model; import com.github.minecraft_ta.totalDebugCompanion.runtime.RuntimeBinding; +import com.github.minecraft_ta.totalDebugCompanion.ui.EditorContext; import com.github.minecraft_ta.totalDebugCompanion.resource.ContentSource; import com.github.minecraft_ta.totalDebugCompanion.resource.ArchiveEntrySource; import com.github.minecraft_ta.totalDebugCompanion.resource.LocalFileSource; -import com.github.minecraft_ta.totalDebugCompanion.CompanionApp; import com.github.minecraft_ta.totalDebugCompanion.resource.FileTypeResolver; import com.github.minecraft_ta.totalDebugCompanion.resource.ResourceFileType; import com.github.minecraft_ta.totalDebugCompanion.ui.components.editors.ResourceViewPanel; @@ -20,15 +20,17 @@ public final class ResourceView implements IEditorPanel { @Override public RuntimeBinding runtimeBinding() { return runtimeBinding; } + private final EditorContext context; private final ContentSource source; private final ResourceFileType fileType; private final ResourceViewPanel panel; - public ResourceView(ContentSource source, RuntimeBinding runtimeBinding) { + public ResourceView(EditorContext context, ContentSource source, RuntimeBinding runtimeBinding) { this.runtimeBinding = runtimeBinding; + this.context = context; this.source = Objects.requireNonNull(source, "source"); this.fileType = FileTypeResolver.resolve(source.displayName()); - this.panel = new ResourceViewPanel(source, this.fileType); + this.panel = new ResourceViewPanel(source, this.fileType, context.navigation()); } public ContentSource source() { @@ -61,7 +63,7 @@ public EditorLocation getLocation() { return EditorLocation.forArchiveEntry(archiveEntry.archivePath(), archiveEntry.entryName()); } if (this.source instanceof LocalFileSource localFile) { - return EditorLocation.forFile(localFile.path(), CompanionApp.getWorkspaceDirectory()); + return EditorLocation.forFile(localFile.path(), context.project().profile().workspaceDirectory()); } return new EditorLocation(this.source.displayName(), java.util.List.of(), this.source.tooltip()); } diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/model/ScriptView.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/model/ScriptView.java index 1b19eed7..57918c4e 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/model/ScriptView.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/model/ScriptView.java @@ -1,6 +1,6 @@ package com.github.minecraft_ta.totalDebugCompanion.model; -import com.github.minecraft_ta.totalDebugCompanion.CompanionApp; +import com.github.minecraft_ta.totalDebugCompanion.ui.EditorContext; import com.github.minecraft_ta.totalDebugCompanion.Icons; import com.github.minecraft_ta.totalDebugCompanion.jdt.JavaSnippetSource; import com.github.minecraft_ta.totalDebugCompanion.navigation.NavigationTarget; @@ -16,18 +16,20 @@ public class ScriptView implements IEditorPanel { public static final String FILE_EXTENSION = ".tdscript"; + private final EditorContext context; private final String text; private final Path path; protected ScriptPanel scriptPanel; - public ScriptView(String scriptName) { + public ScriptView(EditorContext context, String scriptName) { + this.context = context; if (!JavaSnippetSource.isValidClassName(scriptName)) { throw new IllegalArgumentException("Invalid script name: " + scriptName); } - if (!CompanionApp.hasProfile()) { + if (context.project() == null) { throw new IllegalStateException("Open a Minecraft profile before creating scripts"); } - this.path = CompanionApp.instancePaths().scripts().resolve(scriptName + FILE_EXTENSION); + this.path = context.project().paths().scripts().resolve(scriptName + FILE_EXTENSION); try { Files.createDirectories(this.path.getParent()); if (!Files.exists(this.path)) { @@ -81,7 +83,7 @@ public Icon getIcon() { @Override public Component getComponent() { if (this.scriptPanel == null) - this.scriptPanel = new ScriptPanel(this); + this.scriptPanel = new ScriptPanel(context, this); return this.scriptPanel; } diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/model/UsagesView.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/model/UsagesView.java index 1d115f26..ed6f4743 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/model/UsagesView.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/model/UsagesView.java @@ -1,7 +1,7 @@ package com.github.minecraft_ta.totalDebugCompanion.model; import com.github.minecraft_ta.totalDebugCompanion.runtime.RuntimeBinding; -import com.github.minecraft_ta.totalDebugCompanion.CompanionApp; +import com.github.minecraft_ta.totalDebugCompanion.ui.EditorContext; import com.github.minecraft_ta.totalDebugCompanion.Icons; import com.github.minecraft_ta.totalDebugCompanion.jdt.symbol.CodeSymbol; import com.github.minecraft_ta.totalDebugCompanion.navigation.NavigationTarget; @@ -18,11 +18,11 @@ public final class UsagesView implements IEditorPanel { private final CodeSymbol symbol; private final UsagesViewPanel panel; - public UsagesView(CodeSymbol symbol, RuntimeBinding runtimeBinding) { + public UsagesView(EditorContext context, CodeSymbol symbol, RuntimeBinding runtimeBinding) { if (runtimeBinding == null) throw new IllegalStateException("Reference search is unavailable"); this.runtimeBinding = runtimeBinding; this.symbol = Objects.requireNonNull(symbol, "symbol"); - this.panel = new UsagesViewPanel(symbol, runtimeBinding.references()); + this.panel = new UsagesViewPanel(symbol, runtimeBinding.references(), context.navigation()::navigate); } public CodeSymbol symbol() { diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/navigation/NavigationService.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/navigation/NavigationService.java index 63366efb..37576ed6 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/navigation/NavigationService.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/navigation/NavigationService.java @@ -1,7 +1,7 @@ package com.github.minecraft_ta.totalDebugCompanion.navigation; import java.util.function.Predicate; -import com.github.minecraft_ta.totalDebugCompanion.CompanionApp; +import com.github.minecraft_ta.totalDebugCompanion.ui.EditorContext; import com.github.minecraft_ta.totalDebugCompanion.project.ProjectScope; import com.github.minecraft_ta.totalDebugCompanion.runtime.RuntimeBinding; import com.github.minecraft_ta.totalDebugCompanion.bytecode.RuntimeSnapshotBytecodeSource; @@ -45,15 +45,21 @@ public enum Activation { private final MainWindow window; private final EditorTabs tabs; private final FileTreeView fileTree; + private final Supplier editors; private volatile ProjectScope project; private final NavigationState emptyNavigation = new NavigationState(); private NavigationState state() { var scope = project; return scope == null ? emptyNavigation : scope.navigation(); } private record Context(ProjectScope project, RuntimeBinding runtime) { } + private ProjectScope requireProject() { + var scope = project; + if (scope == null) throw new IllegalStateException("No Minecraft project is loaded"); + return scope; + } private Context captureContext() { var scope = project; return new Context(scope, scope == null ? null : scope.runtime()); } private boolean isCurrent(Context captured) { - return captured.project() == project && (project == null - ? !CompanionApp.isSwitching() - : project.isActive() && project.runtime() == captured.runtime()); + ProjectScope selected = project; + return captured.project() == selected && (selected == null + || selected.isActive() && selected.runtime() == captured.runtime()); } private final Action backAction = new AbstractAction("Back") { @Override @@ -68,8 +74,9 @@ public void actionPerformed(ActionEvent event) { } }; - public NavigationService(MainWindow window, EditorTabs tabs, FileTreeView fileTree) { - this.project = CompanionApp.currentScope(); + public NavigationService(MainWindow window, EditorTabs tabs, FileTreeView fileTree, ProjectScope project, Supplier editors) { + this.project = project; + this.editors = editors; this.window = Objects.requireNonNull(window, "window"); this.tabs = Objects.requireNonNull(tabs, "tabs"); this.fileTree = Objects.requireNonNull(fileTree, "fileTree"); @@ -100,6 +107,22 @@ public CompletableFuture navigate(NavigationTarget target, Activation acti return navigation; } + public void revealPackage(String packageName, String ownerClassName) { + if (ownerClassName == null || ownerClassName.isBlank()) { + reportNavigationFailure("JDT could not resolve the class owning package " + packageName); + return; + } + navigate(new NavigationTarget.RuntimePackage(packageName, ownerClassName)); + } + + private void reportNavigationFailure(String message) { + var editor = this.tabs.getSelectedEditor(); + var informationBar = editor == null ? null : editor.getInformationBar(); + if (informationBar != null) { + informationBar.setDefaultInfoText(message); + } + } + public Action backAction() { return this.backAction; } @@ -140,7 +163,7 @@ public CompletableFuture goForward() { private CompletableFuture performNavigation(NavigationTarget target, Activation activation) { CompletableFuture navigation; try { - RuntimeBinding requestedRuntime = CompanionApp.currentRuntime(); + RuntimeBinding requestedRuntime = project == null ? null : project.runtime(); navigation = switch (target) { case NavigationTarget.RuntimeClass runtimeClass -> openRuntimeSource( runtimeClass.binaryName(), @@ -180,12 +203,12 @@ private CompletableFuture performNavigation(NavigationTarget target, Activ case NavigationTarget.SymbolUsages usages -> onEdt(() -> openRuntimeEditor(requestedRuntime, UsagesView.class, view -> view.symbol().equals(usages.symbol()), - () -> new UsagesView(usages.symbol(), requestedRuntime) + () -> new UsagesView(editors.get(), usages.symbol(), requestedRuntime) ).thenAccept(UsagesView::restartSearch), activation); case NavigationTarget.LiteralUsages usages -> onEdt(() -> openRuntimeEditor(requestedRuntime, LiteralUsagesView.class, view -> view.literal().equals(usages.literal()), - () -> new LiteralUsagesView(usages.literal(), requestedRuntime) + () -> new LiteralUsagesView(editors.get(), usages.literal(), requestedRuntime) ).thenAccept(LiteralUsagesView::restartSearch), activation); case NavigationTarget.RuntimePackage runtimePackage -> revealPackage(runtimePackage); case NavigationTarget.ModuleSearch search -> onEdt(() -> { @@ -208,7 +231,7 @@ private CompletableFuture traverseHistory(NavigationHistory.Direction dire } NavigationEntry destination = state().history.destination( direction, - CompanionApp.getActiveRuntimeSignature() + (project == null ? null : project.runtimeSignature()) ); if (destination == null) { state().traversal.set(null); @@ -312,7 +335,7 @@ private NavigationEntry entryForEditor(IEditorPanel editor) { private NavigationEntry entry(NavigationTarget target, NavigationViewState state) { String runtimeSignature = null; if (NavigationEntry.requiresRuntime(target)) { - runtimeSignature = CompanionApp.getActiveRuntimeSignature(); + runtimeSignature = (project == null ? null : project.runtimeSignature()); if (runtimeSignature == null || runtimeSignature.isBlank()) { return null; } @@ -350,7 +373,7 @@ private static boolean sameEditorDestination(NavigationTarget requested, Navigat private void reportFailure(CompletableFuture navigation, NavigationTarget target) { Context context = captureContext(); navigation.whenComplete((ignored, failure) -> { - if (failure != null && isCurrent(context) && !CompanionApp.isSwitching()) { + if (failure != null && isCurrent(context)) { showFailure(target, unwrap(failure)); } }); @@ -358,7 +381,7 @@ private void reportFailure(CompletableFuture navigation, NavigationTarget private void refreshHistoryActions() { SwingUtilities.invokeLater(() -> { - String runtimeSignature = CompanionApp.getActiveRuntimeSignature(); + String runtimeSignature = (project == null ? null : project.runtimeSignature()); boolean available = state().traversal.get() == null; this.backAction.setEnabled(available && state().history.canNavigate( NavigationHistory.Direction.BACK, @@ -384,13 +407,13 @@ private CompletableFuture openRuntimeSource( return service.load(binaryName).thenCompose(source -> { int offset = offsetResolver.applyAsInt(source); return onEdt(() -> { - if (!isCurrent(context) || service != CompanionApp.getDecompilationService()) { + if (!isCurrent(context) || service != requireProject().requireRuntime().decompiler()) { return CompletableFuture.failedFuture(new CancellationException("Runtime changed during source navigation")); } return openRuntimeEditor(installed, CodeView.class, view -> view.getPath().equals(source.path()), - () -> new CodeView(source, offset, SourceFileNavigation.location(source), installed) + () -> new CodeView(editors.get(), source, offset, SourceFileNavigation.location(source), installed) ).thenAccept(view -> { view.navigateToOffset(offset); if (executionLine > 0) { @@ -407,22 +430,21 @@ private CompletableFuture openLocalFile(NavigationTarget.LocalFile target, return CompletableFuture.failedFuture(new IllegalArgumentException("File does not exist: " + path)); } String fileName = path.getFileName().toString(); - Path scripts = CompanionApp.instancePaths().scripts().toAbsolutePath().normalize(); + Path scripts = requireProject().paths().scripts().toAbsolutePath().normalize(); if (path.getParent().equals(scripts) - && fileName.endsWith(ScriptView.FILE_EXTENSION) - && CompanionApp.hasProfile()) { + && fileName.endsWith(ScriptView.FILE_EXTENSION)) { String scriptName = fileName.substring(0, fileName.length() - ScriptView.FILE_EXTENSION.length()); return onEdt(() -> this.tabs.focusOrCreateIfAbsent( ScriptView.class, view -> view.getTitle().equals(fileName), - () -> new ScriptView(scriptName) + () -> new ScriptView(editors.get(), scriptName) ).thenAccept(view -> view.navigateToOffset(target.offset())), activation); } if (fileName.endsWith(".java")) { return onEdt(() -> this.tabs.focusOrCreateIfAbsent( CodeView.class, view -> view.getPath().equals(path), - () -> new CodeView(path, target.offset()) + () -> new CodeView(editors.get(), path, target.offset()) ).thenAccept(view -> view.navigateToOffset(target.offset())), activation); } return openResource(new LocalFileSource(path), activation); @@ -433,7 +455,7 @@ private CompletableFuture openResource(ContentSource source, Activation ac return onEdt(() -> openRuntimeEditor(installed, ResourceView.class, view -> view.source().identity().equals(source.identity()), - () -> new ResourceView(source, installed) + () -> new ResourceView(editors.get(), source, installed) ).thenApply(ignored -> null), activation); } @@ -451,7 +473,7 @@ private CompletableFuture openRuntimeEditor( private CompletableFuture revealPackage(NavigationTarget.RuntimePackage target) { var result = new CompletableFuture(); Context context = captureContext(); - CompanionApp.getCodeInsightService().locateClass(target.ownerClassName(), new CodeInsightService.Listener<>() { + editors.get().insights().locateClass(target.ownerClassName(), new CodeInsightService.Listener<>() { @Override public void onCompleted(RuntimeSnapshotBytecodeSource.Source source) { if (!isCurrent(context)) { result.cancel(false); return; } @@ -514,7 +536,7 @@ private CompletableFuture onEdt( Context context = captureContext(); SwingUtilities.invokeLater(() -> { try { - if (!isCurrent(context) || CompanionApp.isSwitching()) { + if (!isCurrent(context)) { result.completeExceptionally(new CancellationException("Project changed")); return; } @@ -546,7 +568,7 @@ private void showFailure(NavigationTarget target, Throwable failure) { } String message = "Unable to open " + label(target) + ": " + detail; SwingUtilities.invokeLater(() -> { - if (!isCurrent(context) || CompanionApp.isSwitching()) return; + if (!isCurrent(context)) return; JOptionPane.showMessageDialog( this.window, message, diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/project/ProjectScope.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/project/ProjectScope.java index 8e3791cd..41ba63af 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/project/ProjectScope.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/project/ProjectScope.java @@ -21,6 +21,9 @@ /** Resources and request admission for one opened project. */ public final class ProjectScope implements AutoCloseable { public enum Phase { ACTIVE, SWITCHING, RETIRED } + public static final class InactiveProjectException extends IllegalStateException { + private InactiveProjectException() { super("Project changed during the request"); } + } public record PendingNavigation(NavigationTarget target, NavigationService.Activation activation) { } private final NavigationState navigation = new NavigationState(); @@ -50,7 +53,7 @@ public static ProjectScope open(Object lock, CompanionProfile profile) throws IO public Phase phase() { return phase; } public boolean isActive() { return phase == Phase.ACTIVE; } public void requireActive() { - if (!isActive()) throw new IllegalStateException("Project changed during the request"); + if (!isActive()) throw new InactiveProjectException(); } /** Check and submit under the shared lifecycle lock; actions must never wait. */ @@ -67,6 +70,11 @@ public void cancelSwitch() { } public void retire() { synchronized (lock) { phase = Phase.RETIRED; } } public RuntimeBinding runtime() { return runtime; } + public RuntimeBinding requireRuntime() { + RuntimeBinding installed = runtime; + if (installed == null) throw new IllegalStateException("Runtime class index is not ready"); + return installed; + } public String runtimeSignature() { var value = runtime; return value == null ? null : value.snapshot().signature(); } /** Called by the loader under the shared lifecycle lock, before its ownership handoff. */ diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/script/ScriptExecutionService.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/script/ScriptExecutionService.java new file mode 100644 index 00000000..eff1724c --- /dev/null +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/script/ScriptExecutionService.java @@ -0,0 +1,41 @@ +package com.github.minecraft_ta.totalDebugCompanion.script; + +import com.github.minecraft_ta.totalDebugCompanion.project.ProjectScope; +import com.github.minecraft_ta.totalDebugCompanion.project.ProjectScope.InactiveProjectException; +import com.github.minecraft_ta.totalDebugCompanion.session.CompanionSession; +import com.github.minecraft_ta.totaldebug.protocol.execution.ExecutionResult; +import com.github.minecraft_ta.totaldebug.protocol.execution.ScriptExecutionEnvironment; +import com.github.minecraft_ta.totaldebug.protocol.scnet.StopScriptMessage; +import java.util.function.Consumer; +import java.util.function.BooleanSupplier; + +/** Authenticated execution and cancellation, shared by editors and MCP jobs. */ +public final class ScriptExecutionService { + private final CompanionSession session; + private final BooleanSupplier connected; + private final ScriptCompilationService compiler; + + public ScriptExecutionService(CompanionSession session, ScriptCompilationService compiler, BooleanSupplier connected) { + this.connected = connected; + this.session = session; + this.compiler = compiler; + } + + public boolean isConnected() { return connected.getAsBoolean(); } + + public boolean run(ProjectScope project, int id, String source, boolean serverSide, + ScriptExecutionEnvironment environment, Consumer failureHandler) { + if (project == null || !project.isActive() || !isConnected()) return false; + try { + return project.admit(() -> { + if (!isConnected()) return false; + compiler.submit(id, source, serverSide, environment, failureHandler); + return true; + }); + } catch (InactiveProjectException ignored) { + return false; + } + } + + public boolean stop(int id) { return compiler.cancel(id) || session.send(new StopScriptMessage(id)); } +} diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/script/SnippetExecutionService.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/script/SnippetExecutionService.java index 009d19d9..519ca69c 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/script/SnippetExecutionService.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/script/SnippetExecutionService.java @@ -2,7 +2,9 @@ import com.github.minecraft_ta.totaldebug.protocol.execution.ScriptExecutionEnvironment; import com.github.minecraft_ta.totaldebug.protocol.execution.ExecutionResult; -import com.github.minecraft_ta.totalDebugCompanion.CompanionApp; +import com.github.minecraft_ta.totalDebugCompanion.project.ProjectScope; +import com.github.minecraft_ta.totalDebugCompanion.session.CompanionSession; +import java.util.function.Consumer; import com.github.minecraft_ta.totalDebugCompanion.jdt.JavaSnippetSource; import com.github.minecraft_ta.totaldebug.protocol.scnet.ExecutionResultMessage; import java.util.Map; @@ -17,12 +19,16 @@ public final class SnippetExecutionService implements AutoCloseable { private final Map> runs = new ConcurrentHashMap<>(); private volatile boolean closed; - public SnippetExecutionService() { - CompanionApp.SERVER.getMessageBus().listenAlways( - ExecutionResultMessage.class, - this, - this::acceptResult - ); + private final CompanionSession session; + private final ScriptExecutionService scripts; + private final ProjectScope project; + private final Consumer listener = this::acceptResult; + + public SnippetExecutionService(CompanionSession session, ScriptExecutionService scripts, ProjectScope project) { + this.session = session; + this.scripts = scripts; + this.project = project; + session.addExecutionResultListener(this.listener); } public Execution execute( @@ -36,14 +42,15 @@ public Execution execute( if (this.closed) { throw new IllegalStateException("Snippet execution service is closed"); } - if (!CompanionApp.SERVER.isClientConnected()) { + if (!scripts.isConnected()) { throw new IllegalStateException("Minecraft is not connected"); } source.requireExecutableSize(); int id = nextId(); CompletableFuture completion = new CompletableFuture<>(); this.runs.put(id, completion); - boolean sent = CompanionApp.runScript( + boolean sent = scripts.run( + project, id, source.source(), side == Side.SERVER, @@ -67,7 +74,7 @@ private int nextId() { private void cancel(int id) { if (this.runs.containsKey(id)) { - CompanionApp.stopScript(id); + scripts.stop(id); } } @@ -99,9 +106,9 @@ public void close() { return; } this.closed = true; - CompanionApp.SERVER.getMessageBus().unregister(ExecutionResultMessage.class, this); + session.removeExecutionResultListener(this.listener); for (Map.Entry> entry : this.runs.entrySet()) { - CompanionApp.stopScript(entry.getKey()); + scripts.stop(entry.getKey()); entry.getValue().completeExceptionally(new IllegalStateException("Snippet execution service closed")); } this.runs.clear(); diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/session/CompanionSession.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/session/CompanionSession.java index bd4df807..99b68b55 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/session/CompanionSession.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/session/CompanionSession.java @@ -1,6 +1,8 @@ package com.github.minecraft_ta.totalDebugCompanion.session; import com.github.minecraft_ta.totaldebug.protocol.CompanionProtocol; +import com.github.minecraft_ta.totaldebug.protocol.scnet.ExecutionResultMessage; +import java.util.function.Consumer; import com.github.minecraft_ta.totaldebug.protocol.scnet.ProtocolBindings; import com.github.minecraft_ta.totaldebug.storage.CompanionSessionDescriptor; import com.github.minecraft_ta.totaldebug.storage.CompanionLaunchContract; @@ -38,6 +40,10 @@ public interface AttachmentHandler { } public interface Listener { + default void openClass(OpenClassMessage message) { } + + default void focusWindow() { } + default void connecting() { } @@ -91,6 +97,14 @@ public void bindAndPublish(CompanionLaunchConfiguration configuration) throws IO .writeAtomically(configuration.descriptorFile()); } + public void addExecutionResultListener(Consumer listener) { + this.server.getMessageBus().listenAlways(ExecutionResultMessage.class, listener, listener); + } + + public void removeExecutionResultListener(Consumer listener) { + this.server.getMessageBus().unregister(ExecutionResultMessage.class, listener); + } + public void setProjectSelectionHandler(AttachmentHandler handler) { if (this.projectSelections != null) throw new IllegalStateException("Session is already published"); this.projectSelectionHandler = Objects.requireNonNull(handler); @@ -157,9 +171,9 @@ private void registerHandlers() { this.server.getMessageBus().listenAlways(RuntimeInventoryMessage.class, this.listener::runtimeInventory); this.server.getMessageBus().listenAlways(ServerManifestMessage.class, this.listener::serverManifest); this.server.getMessageBus().listenAlways(DebugTargetMessage.class, this.listener::debugTarget); - this.server.getMessageBus().listenAlways(OpenClassMessage.class, message -> com.github.minecraft_ta.totalDebugCompanion.CompanionApp.openClass(message.binaryName(), message.targetType(), message.targetIdentifier())); + this.server.getMessageBus().listenAlways(OpenClassMessage.class, this.listener::openClass); this.server.getMessageBus().listenAlways(FocusWindowMessage.class, message -> - SwingUtilities.invokeLater(com.github.minecraft_ta.totalDebugCompanion.CompanionApp::focusWindow)); + SwingUtilities.invokeLater(this.listener::focusWindow)); this.server.addConnectionListener(new IConnectionListener() { @Override public void onConnected() { diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/EditorContext.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/EditorContext.java new file mode 100644 index 00000000..29cde2b5 --- /dev/null +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/EditorContext.java @@ -0,0 +1,17 @@ +package com.github.minecraft_ta.totalDebugCompanion.ui; + +import com.github.minecraft_ta.totalDebugCompanion.debugger.DebugEngine; +import com.github.minecraft_ta.totalDebugCompanion.debugger.DebuggerSessionController; +import com.github.minecraft_ta.totalDebugCompanion.navigation.NavigationService; +import com.github.minecraft_ta.totalDebugCompanion.project.ProjectScope; +import com.github.minecraft_ta.totalDebugCompanion.script.ScriptExecutionService; +import com.github.minecraft_ta.totalDebugCompanion.search.insight.CodeInsightService; +import com.github.minecraft_ta.totalDebugCompanion.session.CompanionSession; +import java.awt.Window; +import java.util.function.BiConsumer; + +/** The collaborators shared by Java editors in one project. */ +public record EditorContext(Window owner, ProjectScope project, CodeInsightService insights, + DebuggerSessionController debugger, NavigationService navigation, + ScriptExecutionService scripts, CompanionSession session, + BiConsumer inspectVariable) { } diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/editors/AbstractCodeViewPanel.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/editors/AbstractCodeViewPanel.java index 226f9457..0bbcccc1 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/editors/AbstractCodeViewPanel.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/editors/AbstractCodeViewPanel.java @@ -1,6 +1,8 @@ package com.github.minecraft_ta.totalDebugCompanion.ui.components.editors; +import com.github.minecraft_ta.totalDebugCompanion.ui.EditorContext; import com.github.minecraft_ta.totalDebugCompanion.jdt.diagnostics.ASTCache; +import com.github.minecraft_ta.totalDebugCompanion.jdt.diagnostics.JavaEditorSource; import com.github.minecraft_ta.totalDebugCompanion.jdt.diagnostics.CustomJavaLinkGenerator; import com.github.minecraft_ta.totalDebugCompanion.jdt.semanticHighlighting.CustomJavaTokenMaker; import com.github.minecraft_ta.totalDebugCompanion.model.JavaEditorContext; @@ -20,22 +22,25 @@ /** Java-specific parsing and navigation layered on top of the shared text editor. */ public class AbstractCodeViewPanel extends AbstractTextViewPanel implements JavaEditorContext { + protected final EditorContext context; protected final String identifier; private boolean astDisposed; - public AbstractCodeViewPanel(String identifier, String className) { - this(identifier, className, com.github.minecraft_ta.totalDebugCompanion.jdt.diagnostics.JavaEditorSource::identity); + public AbstractCodeViewPanel(EditorContext context, String identifier, String className) { + this(context, identifier, className, JavaEditorSource::identity); } protected AbstractCodeViewPanel( - String identifier, + EditorContext context, String identifier, String className, - Function sourceFactory + Function sourceFactory ) { super(); + this.context = context; + installNavigationHistoryMenu(context.navigation()); this.identifier = identifier; - this.editorPane.setLinkGenerator(new CustomJavaLinkGenerator(identifier)); + this.editorPane.setLinkGenerator(new CustomJavaLinkGenerator(identifier, context.navigation()::revealPackage, target -> context.navigation().navigate(target))); this.editorPane.getDocument().addDocumentListener((DocumentChangeListener) event -> { if (event.getType() == DocumentEvent.EventType.CHANGE) { return; diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/editors/AbstractTextViewPanel.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/editors/AbstractTextViewPanel.java index 8dca3e77..0af8cf26 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/editors/AbstractTextViewPanel.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/editors/AbstractTextViewPanel.java @@ -9,7 +9,7 @@ import com.github.minecraft_ta.totalDebugCompanion.ui.theme.CompanionTheme; import com.github.minecraft_ta.totalDebugCompanion.ui.theme.EditorPalette; import com.github.minecraft_ta.totalDebugCompanion.ui.theme.ThemeManager; -import com.github.minecraft_ta.totalDebugCompanion.ui.views.MainWindow; +import com.github.minecraft_ta.totalDebugCompanion.navigation.NavigationService; import com.github.minecraft_ta.totalDebugCompanion.util.CodeUtils; import com.github.minecraft_ta.totalDebugCompanion.util.UIUtils; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; @@ -83,7 +83,6 @@ public void focusGained(FocusEvent event) { } }); this.editorPane.setSyntaxEditingStyle(RSyntaxTextArea.SYNTAX_STYLE_NONE); - installNavigationHistoryMenu(); add(this.editorLayer, BorderLayout.CENTER); applyTheme(); @@ -97,15 +96,15 @@ public void focusGained(FocusEvent event) { }); } - private void installNavigationHistoryMenu() { + public final void installNavigationHistoryMenu(NavigationService navigation) { JPopupMenu menu = this.editorPane.getPopupMenu(); menu.addSeparator(); - JMenuItem back = menu.add(MainWindow.INSTANCE.navigation().backAction()); + JMenuItem back = menu.add(navigation.backAction()); back.setAccelerator(KeyStroke.getKeyStroke( KeyEvent.VK_LEFT, InputEvent.CTRL_DOWN_MASK | InputEvent.ALT_DOWN_MASK )); - JMenuItem forward = menu.add(MainWindow.INSTANCE.navigation().forwardAction()); + JMenuItem forward = menu.add(navigation.forwardAction()); forward.setAccelerator(KeyStroke.getKeyStroke( KeyEvent.VK_RIGHT, InputEvent.CTRL_DOWN_MASK | InputEvent.ALT_DOWN_MASK diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/editors/CodeViewPanel.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/editors/CodeViewPanel.java index 2fafbfbf..5ac152a0 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/editors/CodeViewPanel.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/editors/CodeViewPanel.java @@ -1,6 +1,6 @@ package com.github.minecraft_ta.totalDebugCompanion.ui.components.editors; -import com.github.minecraft_ta.totalDebugCompanion.CompanionApp; +import com.github.minecraft_ta.totalDebugCompanion.ui.EditorContext; import com.github.minecraft_ta.totalDebugCompanion.GlobalConfig; import com.github.minecraft_ta.totalDebugCompanion.Icons; import com.github.minecraft_ta.totalDebugCompanion.bytecode.insight.HierarchyDirection; @@ -19,7 +19,6 @@ import com.github.minecraft_ta.totalDebugCompanion.search.insight.CodeInsightService; import com.github.minecraft_ta.totalDebugCompanion.ui.views.HierarchyPreviewPopup; import com.github.minecraft_ta.totalDebugCompanion.ui.views.ImplementationChooserPopup; -import com.github.minecraft_ta.totalDebugCompanion.ui.views.MainWindow; import com.github.minecraft_ta.totalDebugCompanion.ui.components.ExpressionCompletionSupport; import com.github.minecraft_ta.totalDebugCompanion.ui.theme.ThemeManager; import com.github.minecraft_ta.totalDebugCompanion.util.CodeUtils; @@ -75,8 +74,8 @@ public class CodeViewPanel extends AbstractCodeViewPanel { private CodeInsightService.SearchHandle actionSearch; private final DebuggerLineHighlights debuggerLineHighlights; - public CodeViewPanel(CodeView codeView) { - super(codeView.getPath().toString(), codeView.getTitle()); + public CodeViewPanel(EditorContext context, CodeView codeView) { + super(context, codeView.getPath().toString(), codeView.getTitle()); this.editorPane.setEditable(false); this.editorPane.setBorder(new CompoundBorder( this.editorPane.getBorder(), @@ -84,10 +83,10 @@ public CodeViewPanel(CodeView codeView) { )); enableSearch(); - this.insightService = CompanionApp.getCodeInsightService(); - this.implementationChooser = new ImplementationChooserPopup(MainWindow.INSTANCE, this.insightService); + this.insightService = context.insights(); + this.implementationChooser = new ImplementationChooserPopup(context.owner(), this.insightService, target -> context.navigation().navigate(target)); this.implementationChooser.setListFont(this.editorPane.getFont()); - this.hierarchyPreview = new HierarchyPreviewPopup(MainWindow.INSTANCE, this.insightService); + this.hierarchyPreview = new HierarchyPreviewPopup(context.owner(), this.insightService); this.hierarchyPreview.setContentFont(this.editorPane.getFont()); this.codeVisionLayerUI = new CodeVisionLayerUI(this.editorPane, new CodeVisionLayerUI.Handler() { @@ -108,7 +107,7 @@ public void showHierarchy( @Override public void showDebuggerValue(DebuggerInlineValueHints.ValueHint value) { - MainWindow.INSTANCE.showDebuggerValue(value.frame(), value.value().variable()); + context.inspectVariable().accept(value.frame(), value.value().variable()); } }); this.codeVisionLayer = new JLayer<>(this.editorLayer, this.codeVisionLayerUI); @@ -185,7 +184,7 @@ public void hidePreview() { this.breakpointMarkers = null; this.debuggerListener = null; } else { - DebuggerSessionController debugger = CompanionApp.getDebuggerController(); + DebuggerSessionController debugger = context.debugger(); debugger.registerSource(this.debugSource); this.breakpointMarkers = new BreakpointGutterMarkers( editorGutter, @@ -337,7 +336,7 @@ public void dispose() { this.hierarchyPreview.dispose(); this.breakpointEditor.dispose(); if (this.debuggerListener != null) { - CompanionApp.getDebuggerController().removeListener(this.debuggerListener); + context.debugger().removeListener(this.debuggerListener); } this.removeInlineAstListener.run(); this.removeDebuggerPresentationListener.run(); @@ -425,7 +424,7 @@ private void toggleBreakpointAtCaret() { } private void toggleBreakpointAtLine(int displayedLine) { - DebuggerSessionController debugger = CompanionApp.getDebuggerController(); + DebuggerSessionController debugger = context.debugger(); Optional request; try { DebuggerSessionController.Breakpoint existing = debugger.breakpoint( @@ -455,7 +454,7 @@ private void toggleBreakpointAtLine(int displayedLine) { } private void toggleBreakpointEnabledAtLine(int displayedLine) { - DebuggerSessionController debugger = CompanionApp.getDebuggerController(); + DebuggerSessionController debugger = context.debugger(); debugger.toggleBreakpointEnabled(this.debugSource, displayedLine) .whenComplete((enabled, failure) -> SwingUtilities.invokeLater(() -> { if (failure != null) { @@ -469,7 +468,7 @@ private void toggleBreakpointEnabledAtLine(int displayedLine) { } private void showBreakpointEditor(int displayedLine, Component invoker, Point location) { - DebuggerSessionController debugger = CompanionApp.getDebuggerController(); + DebuggerSessionController debugger = context.debugger(); DebuggerSessionController.Breakpoint managed = debugger.breakpoint( this.debugSource.uri(), displayedLine @@ -593,7 +592,7 @@ public void mouseReleased(MouseEvent event) { } private void showUsages(CodeSymbol symbol) { - MainWindow.INSTANCE.navigation().navigate(new NavigationTarget.SymbolUsages(symbol)); + context.navigation().navigate(new NavigationTarget.SymbolUsages(symbol)); } private void showImplementations(CodeSymbol symbol, int anchorOffset) { diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/editors/ResourceViewPanel.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/editors/ResourceViewPanel.java index 858212aa..ea519243 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/editors/ResourceViewPanel.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/editors/ResourceViewPanel.java @@ -1,6 +1,7 @@ package com.github.minecraft_ta.totalDebugCompanion.ui.components.editors; import com.github.minecraft_ta.totalDebugCompanion.resource.ContentSource; +import com.github.minecraft_ta.totalDebugCompanion.navigation.NavigationService; import com.github.minecraft_ta.totalDebugCompanion.resource.LoadedResource; import com.github.minecraft_ta.totalDebugCompanion.resource.ResourceFileType; import com.github.minecraft_ta.totalDebugCompanion.resource.ResourceLoader; @@ -21,6 +22,7 @@ public final class ResourceViewPanel extends JPanel { return thread; }); + private final NavigationService navigation; private final ContentSource source; private final ResourceFileType fileType; private final BottomInformationBar informationBar = new BottomInformationBar(); @@ -29,8 +31,9 @@ public final class ResourceViewPanel extends JPanel { private Component activeView; private boolean disposed; - public ResourceViewPanel(ContentSource source, ResourceFileType fileType) { + public ResourceViewPanel(ContentSource source, ResourceFileType fileType, NavigationService navigation) { super(new BorderLayout()); + this.navigation = navigation; this.source = source; this.fileType = fileType; reload(); @@ -75,6 +78,7 @@ private void showContent(LoadedResource content) { case LoadedResource.Text text -> new TextFileViewPanel(text, this.fileType, this.informationBar); case LoadedResource.Image image -> new ImageViewPanel(image, this.informationBar); }; + if (view instanceof AbstractTextViewPanel text) text.installNavigationHistoryMenu(navigation); replaceActiveView(view); } diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/editors/ScriptPanel.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/editors/ScriptPanel.java index 2d1dcdc7..7a357a0a 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/editors/ScriptPanel.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/editors/ScriptPanel.java @@ -1,8 +1,9 @@ package com.github.minecraft_ta.totalDebugCompanion.ui.components.editors; +import com.github.minecraft_ta.totalDebugCompanion.ui.EditorContext; import com.github.minecraft_ta.totaldebug.protocol.execution.ScriptExecutionEnvironment; import com.github.minecraft_ta.totaldebug.protocol.execution.ExecutionStatus; -import com.github.minecraft_ta.totalDebugCompanion.CompanionApp; +import java.util.function.Consumer; import com.github.minecraft_ta.totalDebugCompanion.GlobalConfig; import com.github.minecraft_ta.totalDebugCompanion.Icons; import com.github.minecraft_ta.totalDebugCompanion.jdt.JavaSnippetSource; @@ -17,7 +18,6 @@ import com.github.minecraft_ta.totalDebugCompanion.ui.components.FlatIconButton; import com.github.minecraft_ta.totalDebugCompanion.ui.components.values.ScriptResultTree; import com.github.minecraft_ta.totalDebugCompanion.ui.views.CodeCompletionPopup; -import com.github.minecraft_ta.totalDebugCompanion.ui.views.MainWindow; import com.github.minecraft_ta.totalDebugCompanion.ui.views.SignatureHelpPopup; import com.github.minecraft_ta.totalDebugCompanion.util.UIUtils; import org.eclipse.core.runtime.OperationCanceledException; @@ -50,8 +50,8 @@ public class ScriptPanel extends AbstractCodeViewPanel { private final int scriptId = SCRIPT_ID++; private final ScriptView scriptView; - private final CodeCompletionPopup codeCompletionPopup = new CodeCompletionPopup(MainWindow.INSTANCE); - private final SignatureHelpPopup signatureHelpPopup = new SignatureHelpPopup(MainWindow.INSTANCE); + private final CodeCompletionPopup codeCompletionPopup; + private final SignatureHelpPopup signatureHelpPopup; private final FlatIconButton runButton = new FlatIconButton(Icons.RUN, false); private final FlatIconButton runServerButton = new FlatIconButton(Icons.RUN_SERVER, false); @@ -114,13 +114,15 @@ public Dimension getPreferredSize() { private int lastCaretPos; private JavaSnippetSource.GeneratedSource lastGeneratedSource; - public ScriptPanel(ScriptView scriptView) { + public ScriptPanel(EditorContext context, ScriptView scriptView) { super( - scriptView.getPath().toString(), + context, scriptView.getPath().toString(), scriptView.getScriptName(), text -> JavaSnippetSource.body(scriptView.getScriptName(), text).editorSource() ); this.scriptView = scriptView; + this.codeCompletionPopup = new CodeCompletionPopup(context.owner()); + this.signatureHelpPopup = new SignatureHelpPopup(context.owner()); var headerBar = Box.createHorizontalBox(); headerBar.setBackground(ThemeColors.headerBackground()); @@ -128,7 +130,7 @@ public ScriptPanel(ScriptView scriptView) { runButton.addActionListener(e -> runScript(false)); runServerButton.addActionListener(e -> runScript(true)); - stopButton.addActionListener(e -> CompanionApp.stopScript(this.scriptId)); + stopButton.addActionListener(e -> context.scripts().stop(this.scriptId)); headerBar.add(runButton); headerBar.add(runServerButton); @@ -146,9 +148,9 @@ public ScriptPanel(ScriptView scriptView) { setupAutocompletion(); setupFormatting(); - var messageBus = CompanionApp.SERVER.getMessageBus(); - messageBus.listenAlways(ExecutionResultMessage.class, this, this::acceptResult); - this.unsubscribeResults = () -> messageBus.unregister(ExecutionResultMessage.class, this); + Consumer listener = this::acceptResult; + context.session().addExecutionResultListener(listener); + this.unsubscribeResults = () -> context.session().removeExecutionResultListener(listener); } private void acceptResult(ExecutionResultMessage m) { @@ -178,7 +180,7 @@ private void acceptResult(ExecutionResultMessage m) { } private void runScript(boolean server) { - if (!CompanionApp.SERVER.isClientConnected()) { + if (!context.scripts().isConnected()) { this.bottomInformationBar.setFailureInfoText("Not connected to game client!"); return; } @@ -199,7 +201,8 @@ private void runScript(boolean server) { this.bottomInformationBar.setProcessInfoText("Compiling..."); this.lastGeneratedSource = generated; clearRunOutput(); - if (!CompanionApp.runScript( + if (!context.scripts().run( + context.project(), this.scriptId, this.lastGeneratedSource.source(), server, @@ -338,7 +341,7 @@ private void setupSaveBehavior() { this.saveTimer.setRepeats(false); addHierarchyListener(e -> { if (e.getChangeFlags() == HierarchyEvent.PARENT_CHANGED && getParent() == null) { - CompanionApp.stopScript(this.scriptId); + context.scripts().stop(this.scriptId); this.saveTimer.stop(); } }); diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/editors/UsagesViewPanel.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/editors/UsagesViewPanel.java index cacf4ce3..8c1b6ab6 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/editors/UsagesViewPanel.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/editors/UsagesViewPanel.java @@ -1,5 +1,6 @@ package com.github.minecraft_ta.totalDebugCompanion.ui.components.editors; +import java.util.function.Consumer; import com.github.minecraft_ta.totalDebugCompanion.Icons; import com.github.minecraft_ta.totalDebugCompanion.bytecode.reference.ReferenceLocation; import com.github.minecraft_ta.totalDebugCompanion.bytecode.reference.ReferenceQuery; @@ -11,7 +12,6 @@ import com.github.minecraft_ta.totalDebugCompanion.ui.presentation.RuntimeModulePresentation; import com.github.minecraft_ta.totalDebugCompanion.ui.speedsearch.SpeedSearch; import com.github.minecraft_ta.totalDebugCompanion.ui.theme.DynamicMatteBorder; -import com.github.minecraft_ta.totalDebugCompanion.ui.views.MainWindow; import org.objectweb.asm.Type; import javax.swing.BorderFactory; @@ -51,6 +51,7 @@ public final class UsagesViewPanel extends JPanel { private static final int INITIAL_RESULT_LIMIT = 200; private static final int MAX_RESULT_LIMIT = 5_000; + private final Consumer navigator; private final ReferenceQuery query; private final String targetDisplayName; private final javax.swing.Icon targetIcon; @@ -75,12 +76,12 @@ public final class UsagesViewPanel extends JPanel { private List currentUsages = List.of(); private UsageTreeModel.Options groupingOptions = UsageTreeModel.Options.defaults(); - public UsagesViewPanel(CodeSymbol symbol, ReferenceSearchService searchService) { + public UsagesViewPanel(CodeSymbol symbol, ReferenceSearchService searchService, Consumer navigator) { this( Objects.requireNonNull(symbol, "symbol").referenceQuery(), symbol.displayName(), symbolIcon(symbol), - searchService + searchService, navigator ); } @@ -88,9 +89,10 @@ public UsagesViewPanel( ReferenceQuery query, String targetDisplayName, javax.swing.Icon targetIcon, - ReferenceSearchService searchService + ReferenceSearchService searchService, Consumer navigator ) { super(new BorderLayout()); + this.navigator = navigator; this.query = Objects.requireNonNull(query, "query"); this.targetDisplayName = Objects.requireNonNull(targetDisplayName, "targetDisplayName"); this.targetIcon = Objects.requireNonNull(targetIcon, "targetIcon"); @@ -396,7 +398,7 @@ private void openSelectedUsage() { if (!(nodeValue instanceof UsageNode usage)) { return; } - MainWindow.INSTANCE.navigation().navigate(new NavigationTarget.UsageSite(usage.usage(), this.query)); + navigator.accept(new NavigationTarget.UsageSite(usage.usage(), this.query)); } private void cancelActiveSearch() { diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/global/ApplicationStatusBar.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/global/ApplicationStatusBar.java index a8116e18..42e45b5e 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/global/ApplicationStatusBar.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/global/ApplicationStatusBar.java @@ -1,6 +1,5 @@ package com.github.minecraft_ta.totalDebugCompanion.ui.components.global; -import com.github.minecraft_ta.totalDebugCompanion.CompanionApp; import com.github.minecraft_ta.totalDebugCompanion.Icons; import com.github.minecraft_ta.totalDebugCompanion.jdt.diagnostics.ASTCache; import com.github.minecraft_ta.totalDebugCompanion.model.EditorLocation; @@ -71,7 +70,10 @@ public final class ApplicationStatusBar extends JPanel { null ); - public ApplicationStatusBar(Consumer navigator) { + private final Runnable retryIndex; + + public ApplicationStatusBar(Consumer navigator, Runnable retryIndex) { + this.retryIndex = retryIndex; this.breadcrumbs = new BreadcrumbBar(Objects.requireNonNull(navigator, "navigator")); this.memberDebounce = new Timer(140, event -> refreshMember()); this.memberDebounce.setRepeats(false); @@ -258,7 +260,7 @@ private void showTaskPopup() { if (this.runtimeStatus.phase() == RuntimeIndexService.Phase.FAILED) { popup.addSeparator(); JMenuItem retry = new JMenuItem("Retry class indexing"); - retry.addActionListener(event -> CompanionApp.retryRuntimeIndex()); + retry.addActionListener(event -> retryIndex.run()); popup.add(retry); } popup.show( diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/treeView/FileTreeView.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/treeView/FileTreeView.java index d989661d..c307f3e3 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/treeView/FileTreeView.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/treeView/FileTreeView.java @@ -1,10 +1,10 @@ package com.github.minecraft_ta.totalDebugCompanion.ui.components.treeView; -import com.github.minecraft_ta.totalDebugCompanion.CompanionApp; +import com.github.minecraft_ta.totalDebugCompanion.project.ProjectScope; +import java.util.function.Supplier; import com.github.minecraft_ta.totalDebugCompanion.Icons; import com.github.minecraft_ta.totalDebugCompanion.bytecode.RuntimeSnapshotBytecodeSource; import com.github.minecraft_ta.totalDebugCompanion.navigation.NavigationTarget; -import com.github.minecraft_ta.totalDebugCompanion.model.ScriptView; import com.github.minecraft_ta.totaldebug.storage.RuntimeInventory; import com.github.minecraft_ta.totalDebugCompanion.runtime.RuntimeSourceCatalog; import com.github.minecraft_ta.totalDebugCompanion.ui.components.treeView.lazyFileTree.*; @@ -25,8 +25,11 @@ public class FileTreeView extends JScrollPane { private final LazyFileJTree tree; - public FileTreeView(Consumer navigator) { + private final Supplier project; + + public FileTreeView(Supplier project, Consumer navigator) { super(); + this.project = project; java.util.Objects.requireNonNull(navigator, "navigator"); this.tree = new LazyFileJTree() { @@ -83,7 +86,7 @@ public FileSystemFileItem createFileSystemFileItem(Path path) { setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 3)); } - private static void openItem( + private void openItem( LazyTreeNode node, TreeItem item, Consumer navigator @@ -95,15 +98,7 @@ private static void openItem( if (item instanceof DecompiledSourcesTreeItem.SourceItem source) { navigator.accept(new NavigationTarget.RuntimeClass(source.binaryName())); } else if (item instanceof FileSystemFileItem fileItem) { - String lowerName = fileItem.getName().toLowerCase(Locale.ROOT); - boolean scriptFile = lowerName.endsWith(ScriptView.FILE_EXTENSION); - if (scriptFile - && node.getParent().getUserObject().getName().equals("scripts") - && CompanionApp.hasProfile()) { - navigator.accept(new NavigationTarget.LocalFile(fileItem.getPath())); - } else { - navigator.accept(new NavigationTarget.LocalFile(fileItem.getPath())); - } + navigator.accept(new NavigationTarget.LocalFile(fileItem.getPath())); } else if (item instanceof ZipFileRootItem.Entry entry) { String entryPath = entry.getEntryPath(); if (entryPath.toLowerCase(Locale.ROOT).endsWith(".class")) { @@ -124,26 +119,28 @@ private static void openItem( } } + private RuntimeSourceCatalog sourceCatalog() { + var scope = project.get(); + var runtime = scope == null ? null : scope.runtime(); + return runtime == null ? RuntimeSourceCatalog.empty() : runtime.sources(); + } + public void reloadProfile() { - if (!CompanionApp.hasProfile()) { + var scope = project.get(); + if (scope == null) { this.tree.setRootNodes(); return; } - + var binding = scope.runtime(); + RuntimeSourceCatalog catalog = binding == null ? RuntimeSourceCatalog.empty() : binding.sources(); List rootItems = new ArrayList<>(); - if (CompanionApp.hasProfile()) { - var scripts = this.tree.getItemFactory().createFileSystemDirectoryItem( - CompanionApp.instancePaths().scripts(), - true - ); - scripts.setIcon(FileTreeIcons.forRootDirectory("scripts")); - rootItems.add(scripts); - } - if (!CompanionApp.getRuntimeSourceCatalog().modules().isEmpty()) { - rootItems.add(new DecompiledSourcesTreeItem(this.tree, CompanionApp.getDecompilationService())); + var scripts = this.tree.getItemFactory().createFileSystemDirectoryItem(scope.paths().scripts(), true); + scripts.setIcon(FileTreeIcons.forRootDirectory("scripts")); + rootItems.add(scripts); + if (!catalog.modules().isEmpty()) { + rootItems.add(new DecompiledSourcesTreeItem(this.tree, binding.decompiler())); } - RuntimeSourceCatalog catalog = CompanionApp.getRuntimeSourceCatalog(); if (!catalog.modules().isEmpty()) { var runtime = new DirectoryTreeItem("runtime") { { @@ -192,7 +189,7 @@ public CompletableFuture revealPackage( if (packageName == null || packageName.isBlank()) { throw new IllegalArgumentException("A package name must not be blank"); } - RuntimeSourceCatalog catalog = CompanionApp.getRuntimeSourceCatalog(); + RuntimeSourceCatalog catalog = sourceCatalog(); List path = new ArrayList<>(); if (catalog.sourcesForModule(source.module().id()).size() > 1) { path.add(RuntimeSourceTreeItem.nodeName(source)); @@ -213,7 +210,7 @@ public CompletableFuture revealPackage( public CompletableFuture revealLocalDirectory(Path directory) { Path target = directory.toAbsolutePath().normalize(); for (Path root : List.of( - CompanionApp.instancePaths().scripts() + project.get().paths().scripts() )) { if (!target.startsWith(root)) { continue; @@ -229,7 +226,7 @@ public CompletableFuture revealLocalDirectory(Path directory) { public CompletableFuture revealArchiveDirectory(Path archive, String entryName) { Path normalizedArchive = archive.toAbsolutePath().normalize(); - RuntimeSourceCatalog catalog = CompanionApp.getRuntimeSourceCatalog(); + RuntimeSourceCatalog catalog = sourceCatalog(); for (var module : catalog.modules()) { List sources = catalog.sourcesForModule(module.id()); for (RuntimeSnapshotBytecodeSource.Source source : sources) { diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/CreateScriptWindow.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/CreateScriptWindow.java index 7f2a2fab..7e26e931 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/CreateScriptWindow.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/CreateScriptWindow.java @@ -1,7 +1,7 @@ package com.github.minecraft_ta.totalDebugCompanion.ui.views; +import com.github.minecraft_ta.totalDebugCompanion.ui.EditorContext; import com.formdev.flatlaf.extras.FlatSVGIcon; -import com.github.minecraft_ta.totalDebugCompanion.CompanionApp; import com.github.minecraft_ta.totalDebugCompanion.Icons; import com.github.minecraft_ta.totalDebugCompanion.jdt.JavaSnippetSource; import com.github.minecraft_ta.totalDebugCompanion.model.ScriptView; @@ -23,9 +23,9 @@ public class CreateScriptWindow extends JDialog { - public CreateScriptWindow(EditorTabs editorTabs) { + public CreateScriptWindow(EditorTabs editorTabs, EditorContext context) { super(SwingUtilities.getWindowAncestor(editorTabs)); - if (!CompanionApp.hasProfile()) { + if (context.project() == null) { throw new IllegalStateException("Open a Minecraft profile before creating scripts"); } var header = new JPanel(); @@ -35,7 +35,7 @@ public CreateScriptWindow(EditorTabs editorTabs) { textField.setPreferredSize(new Dimension(150, (int) textField.getPreferredSize().getHeight())); var verifyInput = (Predicate) (s) -> JavaSnippetSource.isValidClassName(s) - && !Files.exists(CompanionApp.instancePaths().scripts() + && !Files.exists(context.project().paths().scripts() .resolve(s + ScriptView.FILE_EXTENSION)); var setIconAndVerify = (Supplier) () -> { var result = verifyInput.test(textField.getText()); @@ -46,7 +46,7 @@ public CreateScriptWindow(EditorTabs editorTabs) { textField.addActionListener((e) -> { if (!setIconAndVerify.get()) return; - editorTabs.openEditorTab(new ScriptView(textField.getText())); + editorTabs.openEditorTab(new ScriptView(context, textField.getText())); dispose(); }); textField.addKeyListener(new KeyAdapter() { diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/EvaluateExpressionWindow.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/EvaluateExpressionWindow.java index cc746b7f..7144977b 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/EvaluateExpressionWindow.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/EvaluateExpressionWindow.java @@ -1,8 +1,9 @@ package com.github.minecraft_ta.totalDebugCompanion.ui.views; +import com.github.minecraft_ta.totalDebugCompanion.ui.EditorContext; +import com.github.minecraft_ta.totalDebugCompanion.navigation.NavigationTarget; import com.github.minecraft_ta.totaldebug.protocol.execution.ScriptExecutionEnvironment; import com.github.minecraft_ta.totaldebug.protocol.execution.ExecutionStatus; -import com.github.minecraft_ta.totalDebugCompanion.CompanionApp; import com.github.minecraft_ta.totalDebugCompanion.GlobalConfig; import com.github.minecraft_ta.totalDebugCompanion.Icons; import com.github.minecraft_ta.totalDebugCompanion.debugger.DebugEngine; @@ -82,14 +83,19 @@ public final class EvaluateExpressionWindow extends JDialog { private boolean evaluationRunning; private boolean cancelPending; - public EvaluateExpressionWindow(Frame owner, SnippetExecutionService executions) { + private final EditorContext editorContext; + private final Runnable refreshSources; + + public EvaluateExpressionWindow(Frame owner, SnippetExecutionService executions, EditorContext editorContext, Runnable refreshSources) { super(owner, "Evaluate Expression", false); + this.editorContext = editorContext; + this.refreshSources = refreshSources; this.executions = executions; - this.history = CompanionApp.instanceState().expressionHistory(); + this.history = editorContext.project().state().expressionHistory(); configureInput(); configureResults(); configureWindow(); - CompanionApp.getDebuggerController().addListener(this.debuggerListener); + editorContext.debugger().addListener(this.debuggerListener); refreshContexts(); } @@ -255,7 +261,7 @@ private void evaluatePaused(String requested) { StringBuilder source = new StringBuilder(); this.expressionSupport.imports().forEach(imported -> source.append("import ").append(imported).append(";\n")); source.append(requested); - var controller = CompanionApp.getDebuggerController(); + var controller = editorContext.debugger(); String pauseId = selected.pauseId(); setRunning(true); this.status.setText("Evaluating in " + frame.name()); @@ -409,7 +415,7 @@ private void saveAsScript() { "Invalid script name", JOptionPane.ERROR_MESSAGE); return; } - Path path = CompanionApp.instancePaths().scripts().resolve(name + ScriptView.FILE_EXTENSION); + Path path = editorContext.project().paths().scripts().resolve(name + ScriptView.FILE_EXTENSION); if (Files.exists(path)) { JOptionPane.showMessageDialog(this, "A script with that name already exists.", "Script exists", JOptionPane.ERROR_MESSAGE); @@ -429,13 +435,8 @@ private void saveAsScript() { "Unable to save script", JOptionPane.ERROR_MESSAGE); return; } - String scriptName = name; - MainWindow.INSTANCE.getEditorTabs().focusOrCreateIfAbsent( - ScriptView.class, - view -> view.getTitle().equals(path.getFileName().toString()), - () -> new ScriptView(scriptName) - ); - MainWindow.INSTANCE.refreshRuntimeSources(); + refreshSources.run(); + editorContext.navigation().navigate(new NavigationTarget.LocalFile(path)); } private void applyTheme() { @@ -454,7 +455,7 @@ private record EvaluationContext(SnippetExecutionService.Side side, String pause private boolean contextAvailable(EvaluationContext context) { if (context == null) return false; if (context.frame() == null) return true; - var snapshot = CompanionApp.getDebuggerController().snapshot(); + var snapshot = editorContext.debugger().snapshot(); return java.util.Objects.equals(context.pauseId(), snapshot.pauseId()) && snapshot.pause() != null && snapshot.pause().frames().stream().anyMatch(frame -> frame.id() == context.frame().id()); } @@ -465,7 +466,7 @@ private void refreshContexts() { this.context.removeAllItems(); this.context.addItem(new EvaluationContext(SnippetExecutionService.Side.CLIENT, null, null, "Client")); this.context.addItem(new EvaluationContext(SnippetExecutionService.Side.SERVER, null, null, "Server")); - var snapshot = CompanionApp.getDebuggerController().snapshot(); + var snapshot = editorContext.debugger().snapshot(); if (snapshot.pause() != null) { for (var frame : snapshot.pause().frames()) { this.context.addItem(new EvaluationContext(null, snapshot.pauseId(), frame, @@ -492,7 +493,7 @@ else if (!this.evaluationRunning && this.status.getText().startsWith("Frame no l this.status.setText(""); } if (selected != null && selected.frame() != null && contextAvailable(selected)) { - var controller = CompanionApp.getDebuggerController(); + var controller = editorContext.debugger(); this.completion.setCompletionProvider((text, caret, explicit) -> controller.completions(text, caret, selected.frame())); this.expression.setSemanticTokenProvider(text -> controller.expressionTokens(text, selected.frame())); } else { @@ -503,7 +504,7 @@ else if (!this.evaluationRunning && this.status.getText().startsWith("Frame no l @Override public void dispose() { clearDebuggerResults(); - CompanionApp.getDebuggerController().removeListener(this.debuggerListener); + editorContext.debugger().removeListener(this.debuggerListener); super.dispose(); } diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/ImplementationChooserPopup.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/ImplementationChooserPopup.java index c7843dc5..c120cb7f 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/ImplementationChooserPopup.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/ImplementationChooserPopup.java @@ -1,5 +1,6 @@ package com.github.minecraft_ta.totalDebugCompanion.ui.views; +import java.util.function.Consumer; import com.github.minecraft_ta.totalDebugCompanion.Icons; import com.github.minecraft_ta.totalDebugCompanion.bytecode.insight.HierarchyDirection; import com.github.minecraft_ta.totalDebugCompanion.bytecode.insight.HierarchyPage; @@ -53,6 +54,7 @@ public final class ImplementationChooserPopup extends BasePopup { private static final String RESULTS_CARD = "results"; private static final String MESSAGE_CARD = "message"; + private final Consumer navigator; private final CodeInsightService service; private final JLabel title = new JLabel(" ", SwingConstants.CENTER); private final JLabel message = new JLabel("Looking up the hierarchy...", SwingConstants.CENTER); @@ -89,8 +91,9 @@ public void keyPressed(KeyEvent event) { } }; - public ImplementationChooserPopup(Window owner, CodeInsightService service) { + public ImplementationChooserPopup(Window owner, CodeInsightService service, Consumer navigator) { super(owner); + this.navigator = navigator; this.service = Objects.requireNonNull(service, "service"); configureUi(); } @@ -321,12 +324,12 @@ private void openSelected() { setVisible(false); } - private static void openResult(HierarchyResult result) { + private void openResult(HierarchyResult result) { switch (result.symbol()) { - case CodeSymbol.ClassSymbol type -> MainWindow.INSTANCE.navigation().navigate( + case CodeSymbol.ClassSymbol type -> navigator.accept( new NavigationTarget.RuntimeClass(type.className()) ); - case CodeSymbol.MethodSymbol method -> MainWindow.INSTANCE.navigation().navigate( + case CodeSymbol.MethodSymbol method -> navigator.accept( new NavigationTarget.RuntimeDeclaration(RuntimeMember.from(method)) ); case CodeSymbol.FieldSymbol ignored -> throw new IllegalStateException( diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/MainWindow.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/MainWindow.java index 23476306..dfc6aa66 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/MainWindow.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/MainWindow.java @@ -1,5 +1,14 @@ package com.github.minecraft_ta.totalDebugCompanion.ui.views; +import com.github.minecraft_ta.totalDebugCompanion.ui.EditorContext; +import com.github.minecraft_ta.totalDebugCompanion.storage.InstanceState; +import com.github.minecraft_ta.totaldebug.protocol.scnet.RetryRuntimeInventoryMessage; +import com.github.minecraft_ta.totalDebugCompanion.project.ProjectScope; +import com.github.minecraft_ta.totalDebugCompanion.search.insight.CodeInsightService; +import com.github.minecraft_ta.totalDebugCompanion.script.ScriptExecutionService; +import com.github.minecraft_ta.totalDebugCompanion.session.CompanionSession; +import java.util.function.Supplier; +import java.util.function.Consumer; import com.github.minecraft_ta.totalDebugCompanion.CompanionApp; import com.github.minecraft_ta.totalDebugCompanion.Icons; import com.github.minecraft_ta.totalDebugCompanion.debugger.DebugEngine; @@ -14,13 +23,14 @@ import com.github.minecraft_ta.totalDebugCompanion.ui.views.debugger.DebuggerActions; import com.github.minecraft_ta.totalDebugCompanion.ui.views.debugger.DebuggerShortcuts; import com.github.minecraft_ta.totalDebugCompanion.ui.views.debugger.DebuggerWindow; +import com.github.minecraft_ta.totalDebugCompanion.ui.views.debugger.DebuggerPanel.FrameNavigation; import com.github.minecraft_ta.totalDebugCompanion.ui.components.global.WorkspacePanel; +import com.github.minecraft_ta.totalDebugCompanion.runtime.RuntimeBinding; import com.github.minecraft_ta.totalDebugCompanion.runtime.RuntimeIndexService; import com.github.minecraft_ta.totalDebugCompanion.ui.components.treeView.FileTreeView; import com.github.minecraft_ta.totalDebugCompanion.ui.components.treeView.FileTreeViewHeader; import com.github.minecraft_ta.totalDebugCompanion.ui.theme.CompanionTheme; import com.github.minecraft_ta.totalDebugCompanion.ui.theme.ThemeManager; -import com.github.minecraft_ta.totalDebugCompanion.util.UIUtils; import javax.swing.*; import java.awt.*; @@ -34,7 +44,7 @@ public class MainWindow extends JFrame implements AWTEventListener { - public static final MainWindow INSTANCE = new MainWindow(); + public static final MainWindow INSTANCE = CompanionApp.createMainWindow(); private final EditorTabs editorTabs = new EditorTabs(); private final FileTreeView fileTreeView; @@ -54,12 +64,33 @@ public class MainWindow extends JFrame implements AWTEventListener { private long lastShiftReleasedTime = 0; private SearchEverywherePopup searchEverywherePopup; - private MainWindow() { + private boolean disposed; + private final Consumer themeListener = this::updateWindowIcon; + private final Supplier project; + private final DebuggerSessionController debugger; + private final CodeInsightService insights; + private final ScriptExecutionService scripts; + private final CompanionSession session; + private final RuntimeIndexService indexLoader; + private final FrameNavigation frameNavigation; + + public MainWindow(Supplier project, DebuggerSessionController debugger, CodeInsightService insights, + ScriptExecutionService scripts, CompanionSession session, RuntimeIndexService indexLoader, FrameNavigation frameNavigation, Runnable exit) { + this.project = project; + this.debugger = debugger; + this.insights = insights; + this.scripts = scripts; + this.session = session; + this.indexLoader = indexLoader; + this.frameNavigation = frameNavigation; setAutoRequestFocus(false); - this.fileTreeView = new FileTreeView(target -> navigation().navigate(target)); - this.navigationService = new NavigationService(this, this.editorTabs, this.fileTreeView); - this.statusBar = new ApplicationStatusBar(target -> this.navigationService.navigate(target)); + this.fileTreeView = new FileTreeView(project, target -> navigation().navigate(target)); + this.navigationService = new NavigationService(this, this.editorTabs, this.fileTreeView, project.get(), this::editorContext); + this.statusBar = new ApplicationStatusBar(target -> this.navigationService.navigate(target), () -> { + indexLoader.waiting("Requesting runtime inventory again"); + session.send(new RetryRuntimeInventoryMessage()); + }); getContentPane().add(new WorkspacePanel( new FileTreeViewHeader(), this.fileTreeView, @@ -75,7 +106,7 @@ private MainWindow() { fileMenu.add(new AbstractAction("Settings...", Icons.SETTINGS) { @Override public void actionPerformed(ActionEvent e) { - new SettingsWindow(MainWindow.this).setVisible(true); + new SettingsWindow(MainWindow.this, project.get() == null ? InstanceState.inMemory() : project.get().state(), debugger).setVisible(true); } }); menuBar.add(fileMenu); @@ -94,7 +125,7 @@ public void actionPerformed(ActionEvent event) { this.newScriptAction = new AbstractAction("New Script", Icons.JAVA_FILE) { @Override public void actionPerformed(ActionEvent e) { - var window = new CreateScriptWindow(editorTabs); + var window = new CreateScriptWindow(editorTabs, editorContext()); window.setVisible(true); window.setLocationRelativeTo(MainWindow.this); } @@ -102,16 +133,17 @@ public void actionPerformed(ActionEvent e) { this.scriptMenu.add(this.newScriptAction); menuBar.add(this.scriptMenu); menuBar.add(Box.createHorizontalGlue()); - DebuggerSessionController debugger = CompanionApp.getDebuggerController(); this.debuggerActions = new DebuggerActions(debugger); this.debuggerShortcuts = new DebuggerShortcuts(this.debuggerActions); this.debuggerShortcuts.install(this); this.debuggerListener = new DebuggerSessionController.Listener() { @Override public void statusChanged(DebuggerSessionController.Status status) { + ProjectScope selected = project.get(); SwingUtilities.invokeLater(() -> { + if (disposed || selected != project.get() || !status.equals(debugger.status())) return; setDebuggerState(status); - if (status.phase() == DebuggerSessionController.Phase.PAUSED) { + if (selected != null && selected.isActive() && status.phase() == DebuggerSessionController.Phase.PAUSED) { debuggerWindow(debugger); } }); @@ -126,12 +158,12 @@ public void statusChanged(DebuggerSessionController.Status status) { addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent event) { - CompanionApp.exit(); + exit.run(); } }); setTitle("TotalDebug Companion"); updateWindowIcon(ThemeManager.current()); - ThemeManager.addThemeChangeListener(this::updateWindowIcon); + ThemeManager.addThemeChangeListener(this.themeListener); refreshProfile(); Toolkit.getDefaultToolkit().addAWTEventListener( @@ -140,6 +172,25 @@ public void windowClosing(WindowEvent event) { ); } + @Override public void dispose() { + if (!disposed) { + disposed = true; + debugger.removeListener(debuggerListener); + debuggerActions.close(); + debuggerShortcuts.close(); + ThemeManager.removeThemeChangeListener(themeListener); + Toolkit.getDefaultToolkit().removeAWTEventListener(this); + closeProjectWindows(); + editorTabs.closeMatching(editor -> true); + statusBar.setEditor(null); + } + super.dispose(); + } + + public EditorContext editorContext() { + return new EditorContext(this, project.get(), insights, debugger, navigation(), scripts, session, this::showDebuggerValue); + } + private void updateWindowIcon(CompanionTheme theme) { setIconImages(Icons.createWindowIconImages(theme)); } @@ -220,11 +271,12 @@ private void setDebuggerState(DebuggerSessionController.Status status) { private DebuggerWindow debuggerWindow(DebuggerSessionController debugger) { if (this.debuggerWindow == null) { this.debuggerWindow = new DebuggerWindow( + project.get().state(), this, debugger, this.debuggerActions, this.debuggerShortcuts, - (frame, activateEditor) -> CompanionApp.openDebugFrame(frame, activateEditor), + this.frameNavigation, () -> breakpointsWindow(debugger).showWindow(), target -> this.navigationService.navigate(target) ); @@ -245,17 +297,17 @@ private BreakpointsWindow breakpointsWindow(DebuggerSessionController debugger) private EvaluateExpressionWindow evaluateExpressionWindow() { if (this.snippetExecutions == null) { - this.snippetExecutions = new SnippetExecutionService(); + this.snippetExecutions = new SnippetExecutionService(session, scripts, project.get()); } if (this.evaluateExpressionWindow == null) { - this.evaluateExpressionWindow = new EvaluateExpressionWindow(this, this.snippetExecutions); + this.evaluateExpressionWindow = new EvaluateExpressionWindow(this, this.snippetExecutions, editorContext(), this::refreshRuntimeSources); } return this.evaluateExpressionWindow; } public void showDebuggerValue(DebugEngine.StackFrame frame, DebugEngine.Variable variable) { SwingUtilities.invokeLater(() -> - debuggerWindow(CompanionApp.getDebuggerController()).showVariable(frame, variable)); + debuggerWindow(debugger).showVariable(frame, variable)); } @Override @@ -282,7 +334,7 @@ public void eventDispatched(AWTEvent event) { } this.lastShiftReleasedTime = 0; - if (!CompanionApp.hasProfile()) { + if (project.get() == null) { return; } openSearchEverywhere(); @@ -329,18 +381,23 @@ private void handleHistoryMouseButton(MouseEvent event) { public void openSearchEverywhere() { if (this.searchEverywherePopup == null) { - this.searchEverywherePopup = new SearchEverywherePopup(); + this.searchEverywherePopup = new SearchEverywherePopup(this, indexLoader, this::searchRuntime, target -> navigation().navigate(target)); } this.searchEverywherePopup.open(); } public void openSearchEverywhere(NavigationTarget.ModuleSearch search) { if (this.searchEverywherePopup == null) { - this.searchEverywherePopup = new SearchEverywherePopup(); + this.searchEverywherePopup = new SearchEverywherePopup(this, indexLoader, this::searchRuntime, target -> navigation().navigate(target)); } this.searchEverywherePopup.open(search.moduleIds(), search.query()); } + private RuntimeBinding searchRuntime() { + ProjectScope scope = project.get(); + return scope == null ? null : scope.runtime(); + } + public EditorTabs getEditorTabs() { return this.editorTabs; } @@ -349,24 +406,9 @@ public NavigationService navigation() { return this.navigationService; } - public void revealPackage(String packageName, String ownerClassName) { - if (ownerClassName == null || ownerClassName.isBlank()) { - reportNavigationFailure("JDT could not resolve the class owning package " + packageName); - return; - } - this.navigationService.navigate(new NavigationTarget.RuntimePackage(packageName, ownerClassName)); - } - - private void reportNavigationFailure(String message) { - var editor = this.editorTabs.getSelectedEditor(); - var informationBar = editor == null ? null : editor.getInformationBar(); - if (informationBar != null) { - informationBar.setDefaultInfoText(message); - } - } - public void refreshProfile() { - this.navigationService.projectChanged(CompanionApp.currentScope()); + setDebuggerState(debugger.status()); + this.navigationService.projectChanged(project.get()); this.fileTreeView.reloadProfile(); refreshActions(); } @@ -382,6 +424,13 @@ public boolean closeProjectViews() { if (!SwingUtilities.isEventDispatchThread()) throw new IllegalStateException("Project views must close on the EDT"); this.editorTabs.closeMatching(editor -> true); if (this.editorTabs.getTabCount() != 0) return false; + closeProjectWindows(); + this.statusBar.setEditor(null); + setEnabled(false); + return true; + } + + private void closeProjectWindows() { for (Window window : getOwnedWindows()) window.dispose(); if (this.debuggerWindow != null) this.debuggerWindow.dispose(); if (this.breakpointsWindow != null) this.breakpointsWindow.dispose(); @@ -393,9 +442,6 @@ public boolean closeProjectViews() { this.evaluateExpressionWindow = null; this.searchEverywherePopup = null; this.snippetExecutions = null; - this.statusBar.setEditor(null); - setEnabled(false); - return true; } public void refreshRuntimeSources() { @@ -407,9 +453,9 @@ public void refreshRuntimeSources() { } private void refreshActions() { - boolean hasProfile = CompanionApp.hasProfile(); + boolean hasProfile = project.get() != null; this.scriptMenu.setVisible(hasProfile); - this.evaluateExpressionAction.setEnabled(CompanionApp.isConnected()); + this.evaluateExpressionAction.setEnabled(scripts.isConnected()); this.newScriptAction.setEnabled(hasProfile); this.debuggerState.setVisible(hasProfile); } diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/SearchEverywherePopup.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/SearchEverywherePopup.java index e7db8034..a09478b9 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/SearchEverywherePopup.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/SearchEverywherePopup.java @@ -1,8 +1,10 @@ package com.github.minecraft_ta.totalDebugCompanion.ui.views; -import com.github.minecraft_ta.totalDebugCompanion.CompanionApp; +import com.github.minecraft_ta.totalDebugCompanion.runtime.RuntimeBinding; +import java.util.function.Supplier; +import java.util.function.Consumer; +import java.awt.Window; import com.github.minecraft_ta.totalDebugCompanion.Icons; -import com.github.minecraft_ta.totalDebugCompanion.jdt.CompanionClassIndex; import com.github.minecraft_ta.totalDebugCompanion.navigation.NavigationTarget; import com.github.minecraft_ta.totalDebugCompanion.navigation.RuntimeMember; import com.github.minecraft_ta.totalDebugCompanion.runtime.RuntimeIndexService; @@ -90,15 +92,16 @@ public void actionPerformed(ActionEvent event) { private final ModuleFilterPopup moduleFilterPopup; private final Consumer themeListener = theme -> applyTheme(); - private final Consumer indexStatusListener = status -> SwingUtilities.invokeLater(() -> { - if (!isDisplayable()) return; - if (CompanionClassIndex.isOpen()) { - syncRuntimeModules(); - refreshResults(); - } else { - showIndexStatus(status); - } - }); + private final Consumer indexStatusListener = this::indexStatusChanged; + + private void indexStatusChanged(RuntimeIndexService.Status status) { + SwingUtilities.invokeLater(() -> { + if (!isDisplayable()) return; + RuntimeBinding installed = runtime.get(); + if (installed != null) { syncRuntimeModules(installed); refreshResults(); } + else showIndexStatus(status); + }); + } private RuntimeSourceCatalog sourceCatalog = RuntimeSourceCatalog.empty(); private List modules = List.of(); @@ -110,7 +113,16 @@ public void actionPerformed(ActionEvent event) { private Point dragWindowOrigin; private boolean manuallyPositioned; - SearchEverywherePopup() { + private final Window owner; + private final RuntimeIndexService indexLoader; + private final Supplier runtime; + private final Consumer navigator; + SearchEverywherePopup(Window owner, RuntimeIndexService indexLoader, Supplier runtime, + Consumer navigator) { + this.owner = owner; + this.indexLoader = indexLoader; + this.runtime = runtime; + this.navigator = navigator; this.moduleFilterPopup = new ModuleFilterPopup( this.modules, this.selectedModuleIds, @@ -140,7 +152,7 @@ public void actionPerformed(ActionEvent event) { JComponent.WHEN_IN_FOCUSED_WINDOW ); - CompanionApp.addRuntimeIndexStatusListener(this.indexStatusListener); + indexLoader.addStatusListener(this.indexStatusListener); ((JPanel) getContentPane()).setBorder(PopupChrome.border()); setUndecorated(true); @@ -160,12 +172,11 @@ void open() { toFront(); return; } - if (CompanionClassIndex.isOpen()) { - syncRuntimeModules(); - } + RuntimeBinding installed = runtime.get(); + if (installed != null) syncRuntimeModules(installed); setVisible(true); if (!this.manuallyPositioned) { - UIUtils.centerJFrame(this); + UIUtils.centerJFrame(this, owner == null ? this : owner); } this.searchTextField.requestFocusInWindow(); this.searchTextField.selectAll(); @@ -175,9 +186,8 @@ void open() { void open(Set moduleIds, String query) { Objects.requireNonNull(moduleIds, "moduleIds"); Objects.requireNonNull(query, "query"); - if (CompanionClassIndex.isOpen()) { - syncRuntimeModules(); - } + RuntimeBinding installed = runtime.get(); + if (installed != null) syncRuntimeModules(installed); setSelectedModules(moduleIds); this.searchTextField.setText(query); open(); @@ -185,7 +195,7 @@ void open(Set moduleIds, String query) { @Override public void dispose() { - CompanionApp.removeRuntimeIndexStatusListener(this.indexStatusListener); + indexLoader.removeStatusListener(this.indexStatusListener); ThemeManager.removeThemeChangeListener(this.themeListener); this.searchGeneration.incrementAndGet(); if (this.pendingSearch != null) { @@ -321,7 +331,9 @@ private void configureFilterButton() { this.moduleFilterButton.setIconTextGap(6); this.moduleFilterButton.setToolTipText("Filter search results by module"); this.moduleFilterButton.addActionListener(event -> { - syncRuntimeModules(); + RuntimeBinding installed = runtime.get(); + if (installed == null) return; + syncRuntimeModules(installed); this.moduleFilterPopup.updateModules(this.modules, this.selectedModuleIds); this.moduleFilterPopup.show( this.moduleFilterButton, @@ -391,8 +403,8 @@ private void setSelectedModules(Set moduleIds) { refreshResults(); } - private void syncRuntimeModules() { - RuntimeSourceCatalog currentCatalog = CompanionApp.getReferenceSearchService().sourceCatalog(); + private void syncRuntimeModules(RuntimeBinding installed) { + RuntimeSourceCatalog currentCatalog = installed.sources(); List currentModules = currentCatalog.modules(); if (!currentModules.equals(this.modules)) { this.sourceCatalog = currentCatalog; @@ -433,8 +445,9 @@ private void refreshResults() { this.pendingSearch.cancel(false); this.pendingSearch = null; } - if (!CompanionClassIndex.isOpen()) { - showIndexStatus(CompanionApp.getRuntimeIndexStatus()); + RuntimeBinding installed = runtime.get(); + if (installed == null) { + showIndexStatus(indexLoader.status()); return; } @@ -456,6 +469,7 @@ private void refreshResults() { return; } + if (sourceCatalog != installed.sources()) syncRuntimeModules(installed); Category requestedCategory = this.category; int[] sourceIds = this.selectedModuleIds.size() == this.modules.size() ? null @@ -464,16 +478,17 @@ private void refreshResults() { this.resultCount.setText("Searching…"); this.pendingSearch = this.searchExecutor.schedule(() -> { try { + if (runtime.get() != installed) return; List results = this.search.search( - CompanionClassIndex.get(), + installed.snapshot().index(), query, requestedCategory, RESULT_LIMIT, sourceIds ); - SwingUtilities.invokeLater(() -> applyResults(generation, results)); + SwingUtilities.invokeLater(() -> { if (runtime.get() == installed) applyResults(generation, results); }); } catch (RuntimeException failure) { - SwingUtilities.invokeLater(() -> showSearchFailure(generation, failure)); + SwingUtilities.invokeLater(() -> { if (runtime.get() == installed) showSearchFailure(generation, failure); }); } }, 70, TimeUnit.MILLISECONDS); } @@ -543,10 +558,10 @@ private void openSelectedResult() { } setVisible(false); switch (selected) { - case ClassResult type -> MainWindow.INSTANCE.navigation().navigate( + case ClassResult type -> navigator.accept( new NavigationTarget.RuntimeClass(type.binaryName()) ); - case SymbolResult symbol -> MainWindow.INSTANCE.navigation().navigate( + case SymbolResult symbol -> navigator.accept( new NavigationTarget.RuntimeDeclaration(symbol.kind() == SymbolKind.FIELD ? new RuntimeMember.Field(symbol.ownerBinaryName(), symbol.name()) : new RuntimeMember.Method( @@ -555,7 +570,7 @@ private void openSelectedResult() { symbol.descriptor() )) ); - case TextResult text -> MainWindow.INSTANCE.navigation().navigate( + case TextResult text -> navigator.accept( new NavigationTarget.LiteralUsages(text.value()) ); } diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/SettingsWindow.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/SettingsWindow.java index 92a4008b..22757402 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/SettingsWindow.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/SettingsWindow.java @@ -1,7 +1,8 @@ package com.github.minecraft_ta.totalDebugCompanion.ui.views; +import com.github.minecraft_ta.totalDebugCompanion.storage.InstanceState; +import com.github.minecraft_ta.totalDebugCompanion.debugger.DebuggerSessionController; import com.github.minecraft_ta.totalDebugCompanion.GlobalConfig; -import com.github.minecraft_ta.totalDebugCompanion.CompanionApp; import com.github.minecraft_ta.totalDebugCompanion.ui.theme.CompanionTheme; import com.github.minecraft_ta.totalDebugCompanion.ui.theme.ThemeManager; @@ -35,8 +36,13 @@ */ public class SettingsWindow extends JDialog { - public SettingsWindow(Window owner) { + private final InstanceState state; + private final DebuggerSessionController debugger; + + public SettingsWindow(Window owner, InstanceState state, DebuggerSessionController debugger) { super(owner, "Settings", ModalityType.MODELESS); + this.state = state; + this.debugger = debugger; JPanel content = new JPanel(new BorderLayout(0, 12)); content.setBorder(BorderFactory.createEmptyBorder(12, 14, 12, 14)); @@ -59,13 +65,13 @@ public SettingsWindow(Window owner) { addSection(form, row++, "Debugger"); addWideRow(form, row++, createExceptionBreakpointToggle( "Pause on caught exceptions", - CompanionApp.instanceState().breakOnCaughtExceptions(), - CompanionApp.instanceState()::setBreakOnCaughtExceptions + state.breakOnCaughtExceptions(), + state::setBreakOnCaughtExceptions )); addWideRow(form, row++, createExceptionBreakpointToggle( "Pause on uncaught exceptions", - CompanionApp.instanceState().breakOnUncaughtExceptions(), - CompanionApp.instanceState()::setBreakOnUncaughtExceptions + state.breakOnUncaughtExceptions(), + state::setBreakOnUncaughtExceptions )); addWideRow(form, row++, createToggle( "Show inline values while paused", @@ -123,10 +129,10 @@ private JComponent createExceptionBreakpointToggle( JCheckBox toggle = new JCheckBox(label, selected); toggle.addActionListener(event -> { setter.accept(toggle.isSelected()); - CompanionApp.getDebuggerController() + debugger .setExceptionBreakpoints( - CompanionApp.instanceState().breakOnCaughtExceptions(), - CompanionApp.instanceState().breakOnUncaughtExceptions() + state.breakOnCaughtExceptions(), + state.breakOnUncaughtExceptions() ); }); return toggle; diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerExpressionModel.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerExpressionModel.java index 9fad7611..623c734e 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerExpressionModel.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerExpressionModel.java @@ -1,6 +1,6 @@ package com.github.minecraft_ta.totalDebugCompanion.ui.views.debugger; -import com.github.minecraft_ta.totalDebugCompanion.CompanionApp; +import com.github.minecraft_ta.totalDebugCompanion.storage.InstanceState; import com.github.minecraft_ta.totalDebugCompanion.debugger.DebuggerValueLease; import com.github.minecraft_ta.totalDebugCompanion.ui.views.debugger.DebuggerValueTree.DebugValue; @@ -31,7 +31,12 @@ static Outcome failure(String failure) { } } - private final Set watches = new LinkedHashSet<>(CompanionApp.instanceState().debuggerWatches()); + private final InstanceState state; + private final Set watches; + DebuggerExpressionModel(InstanceState state) { + this.state = state; + this.watches = new LinkedHashSet<>(state.debuggerWatches()); + } private Set submitted = new HashSet<>(); private Map outcomes = new HashMap<>(); private final Map frames = new HashMap<>(); @@ -162,6 +167,6 @@ void clearSession() { } private void persistWatches() { - CompanionApp.instanceState().setDebuggerWatches(List.copyOf(this.watches)); + state.setDebuggerWatches(List.copyOf(this.watches)); } } diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerInspector.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerInspector.java index 60e4338f..b9fc40a0 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerInspector.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerInspector.java @@ -1,5 +1,6 @@ package com.github.minecraft_ta.totalDebugCompanion.ui.views.debugger; +import com.github.minecraft_ta.totalDebugCompanion.storage.InstanceState; import com.github.minecraft_ta.totalDebugCompanion.GlobalConfig; import com.github.minecraft_ta.totalDebugCompanion.Icons; import com.github.minecraft_ta.totalDebugCompanion.debugger.DebugEngine; @@ -107,7 +108,7 @@ CompletableFuture inspect( private final JavaExpressionField expression = new JavaExpressionField(); private final ExpressionCompletionSupport expressionCompletion = new ExpressionCompletionSupport(this.expression); private final JButton addWatch = createAddWatchButton(); - private final DebuggerExpressionModel expressions = new DebuggerExpressionModel(); + private final DebuggerExpressionModel expressions; private final Map expressionNodes = new LinkedHashMap<>(); private final PropertyChangeListener previewSettingsListener = event -> onEventThread(this::refreshPreviewMode); @@ -123,10 +124,10 @@ CompletableFuture inspect( private boolean watchSequenceStopped; DebuggerInspector( - DebuggerSessionController controller, + InstanceState state, DebuggerSessionController controller, Consumer navigation ) { - this(controller, navigation, new RuntimeAccess() { + this(state, controller, navigation, new RuntimeAccess() { @Override public CompletableFuture retainValue(String pauseId, int reference) { return controller.retainValue(pauseId, reference); } @@ -167,11 +168,12 @@ public CompletableFuture inspect( } DebuggerInspector( - DebuggerSessionController controller, + InstanceState state, DebuggerSessionController controller, Consumer navigation, RuntimeAccess runtime ) { super(new BorderLayout()); + this.expressions = new DebuggerExpressionModel(state); this.controller = Objects.requireNonNull(controller, "controller"); this.navigation = Objects.requireNonNull(navigation, "navigation"); this.runtime = Objects.requireNonNull(runtime, "runtime"); diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerPanel.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerPanel.java index b6e74631..e6fb4072 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerPanel.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerPanel.java @@ -1,5 +1,6 @@ package com.github.minecraft_ta.totalDebugCompanion.ui.views.debugger; +import com.github.minecraft_ta.totalDebugCompanion.storage.InstanceState; import com.github.minecraft_ta.totalDebugCompanion.Icons; import com.github.minecraft_ta.totalDebugCompanion.debugger.DebugEngine; import com.github.minecraft_ta.totalDebugCompanion.debugger.DebuggerSessionController; @@ -78,17 +79,17 @@ public void breakpointsMutedChanged(boolean muted) { private boolean disposed; DebuggerPanel( - DebuggerSessionController controller, + InstanceState state, DebuggerSessionController controller, DebuggerActions debuggerActions, FrameNavigation frameNavigation ) { - this(controller, debuggerActions, frameNavigation, () -> { + this(state, controller, debuggerActions, frameNavigation, () -> { }, target -> { }); } DebuggerPanel( - DebuggerSessionController controller, + InstanceState state, DebuggerSessionController controller, DebuggerActions debuggerActions, FrameNavigation frameNavigation, Runnable showBreakpoints, @@ -99,7 +100,7 @@ public void breakpointsMutedChanged(boolean muted) { this.debuggerActions = Objects.requireNonNull(debuggerActions, "debuggerActions"); this.frameNavigation = Objects.requireNonNull(frameNavigation, "frameNavigation"); this.frames = new DebuggerFramesPane(this::selectFrame, frameNavigation); - this.inspector = new DebuggerInspector(controller, navigation); + this.inspector = new DebuggerInspector(state, controller, navigation); this.attach = toolbarButton(debuggerActions.attach()); this.resume = toolbarButton(debuggerActions.resume()); diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerWindow.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerWindow.java index 6bfd596c..141b2bc5 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerWindow.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerWindow.java @@ -1,5 +1,6 @@ package com.github.minecraft_ta.totalDebugCompanion.ui.views.debugger; +import com.github.minecraft_ta.totalDebugCompanion.storage.InstanceState; import com.github.minecraft_ta.totalDebugCompanion.GlobalConfig; import com.github.minecraft_ta.totalDebugCompanion.debugger.DebugEngine; import com.github.minecraft_ta.totalDebugCompanion.debugger.DebuggerSessionController; @@ -28,36 +29,36 @@ public final class DebuggerWindow extends JFrame { private final DebuggerSessionController.Listener listener = new DebuggerSessionController.Listener() { @Override public void paused(DebuggerSessionController.PausedState state) { - javax.swing.SwingUtilities.invokeLater(() -> showWindow()); + javax.swing.SwingUtilities.invokeLater(() -> { if (!disposed) showWindow(); }); } }; private boolean disposed; public DebuggerWindow( - Window owner, + InstanceState state, Window owner, DebuggerSessionController controller, DebuggerActions debuggerActions, DebuggerShortcuts debuggerShortcuts, DebuggerPanel.FrameNavigation frameNavigation ) { - this(owner, controller, debuggerActions, debuggerShortcuts, frameNavigation, () -> { + this(state, owner, controller, debuggerActions, debuggerShortcuts, frameNavigation, () -> { }); } public DebuggerWindow( - Window owner, + InstanceState state, Window owner, DebuggerSessionController controller, DebuggerActions debuggerActions, DebuggerShortcuts debuggerShortcuts, DebuggerPanel.FrameNavigation frameNavigation, Runnable showBreakpoints ) { - this(owner, controller, debuggerActions, debuggerShortcuts, frameNavigation, showBreakpoints, target -> { + this(state, owner, controller, debuggerActions, debuggerShortcuts, frameNavigation, showBreakpoints, target -> { }); } public DebuggerWindow( - Window owner, + InstanceState state, Window owner, DebuggerSessionController controller, DebuggerActions debuggerActions, DebuggerShortcuts debuggerShortcuts, @@ -68,7 +69,7 @@ public DebuggerWindow( super("Minecraft Debugger"); this.controller = controller; this.debuggerShortcuts = Objects.requireNonNull(debuggerShortcuts, "debuggerShortcuts"); - this.panel = new DebuggerPanel(controller, debuggerActions, frameNavigation, showBreakpoints, navigation); + this.panel = new DebuggerPanel(state, controller, debuggerActions, frameNavigation, showBreakpoints, navigation); this.debuggerShortcuts.install(this); if (owner instanceof Frame frame) { setIconImages(frame.getIconImages()); diff --git a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/util/UIUtils.java b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/util/UIUtils.java index d6d92925..071e94ef 100644 --- a/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/util/UIUtils.java +++ b/companion/src/main/java/com/github/minecraft_ta/totalDebugCompanion/util/UIUtils.java @@ -1,6 +1,5 @@ package com.github.minecraft_ta.totalDebugCompanion.util; -import com.github.minecraft_ta.totalDebugCompanion.ui.views.MainWindow; import com.github.minecraft_ta.totalDebugCompanion.ui.PopupChrome; import org.fife.ui.rtextarea.RTextScrollPane; @@ -37,8 +36,8 @@ public static void focusWindow(JFrame frame) { WindowsWindowActivator.activate(frame); } - public static void centerJFrame(JFrame frame) { - var gc = MainWindow.INSTANCE.getGraphicsConfiguration(); + public static void centerJFrame(JFrame frame, Window reference) { + var gc = reference.getGraphicsConfiguration(); var dim = gc.getBounds(); frame.setLocation(PopupChrome.centeredLocation(dim, frame.getSize())); } diff --git a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ProjectSwitchLifecycleTest.java b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ProjectSwitchLifecycleTest.java index ca9c5678..6a22908d 100644 --- a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ProjectSwitchLifecycleTest.java +++ b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ProjectSwitchLifecycleTest.java @@ -1,5 +1,7 @@ package com.github.minecraft_ta.totalDebugCompanion; +import com.github.minecraft_ta.totalDebugCompanion.ui.components.global.EditorTabs; +import com.github.minecraft_ta.totalDebugCompanion.ui.components.treeView.FileTreeView; import com.github.minecraft_ta.totalDebugCompanion.session.CompanionLaunchConfiguration; import com.github.minecraft_ta.totalDebugCompanion.project.ProjectScope; import com.github.minecraft_ta.totalDebugCompanion.storage.InstanceState; @@ -16,6 +18,8 @@ import java.util.concurrent.CompletableFuture; import java.util.Map; import com.github.minecraft_ta.totalDebugCompanion.mcp.ProjectSwitchJobs; +import com.github.minecraft_ta.totalDebugCompanion.script.ScriptExecutionService; +import com.github.minecraft_ta.totalDebugCompanion.script.ScriptCompilationService; import com.github.minecraft_ta.totalDebugCompanion.mcp.CodeModeJobService; import java.util.List; import java.util.concurrent.ExecutionException; @@ -36,7 +40,7 @@ class ProjectSwitchLifecycleTest { "-Djava.awt.headless=false", "-cp", classpath, getClass().getName(), directory.toString()).redirectErrorStream(true).redirectOutput(log.toFile()).start(); try { - assertTrue(process.waitFor(30, TimeUnit.SECONDS), () -> "Switch did not finish: " + read(log)); + assertTrue(process.waitFor(180, TimeUnit.SECONDS), () -> "Switch did not finish: " + read(log)); assertEquals(0, process.exitValue(), () -> read(log)); } finally { if (process.isAlive()) process.destroyForcibly(); } } @@ -54,7 +58,8 @@ public static void main(String[] args) { var session = new com.github.minecraft_ta.totalDebugCompanion.session.CompanionSession("test-token"); session.bindAndPublish(new CompanionLaunchConfiguration(paths.home())); set("session", session); - CompanionApp.SERVER = session.server(); + set("scriptExecutions", new ScriptExecutionService(session, (ScriptCompilationService) get("scriptCompiler"), CompanionApp::isConnected)); + var jobs = ProjectSwitchJobs.create(); var constructor = com.github.minecraft_ta.totalDebugCompanion.mcp.CompanionMcpServer.class.getDeclaredConstructor( Path.class, com.github.minecraft_ta.totalDebugCompanion.mcp.CodeModeJobService.class, int.class); @@ -244,14 +249,14 @@ private static void verifyNavigationReset(com.github.minecraft_ta.totalDebugComp var pending = new java.util.concurrent.atomic.AtomicReference>(); var created = new CompletableFuture(); javax.swing.SwingUtilities.invokeAndWait(() -> { - var tree = new com.github.minecraft_ta.totalDebugCompanion.ui.components.treeView.FileTreeView(ignored -> { }) { + var tree = new FileTreeView(CompanionApp::currentScope, ignored -> { }) { @Override public CompletableFuture revealLocalDirectory(Path path) { var delayed = pending.getAndSet(null); return delayed == null ? CompletableFuture.completedFuture(true) : delayed; } }; created.complete(new NavigationService(window, - new com.github.minecraft_ta.totalDebugCompanion.ui.components.global.EditorTabs(), tree)); + new EditorTabs(), tree, CompanionApp.currentScope(), window::editorContext)); }); var navigation = created.join(); var scopeA = new ProjectScope(new Object(), CompanionApp.currentProject(), InstanceState.inMemory()); diff --git a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/RuntimeInstallationTest.java b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/RuntimeInstallationTest.java index 643507ab..be354c32 100644 --- a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/RuntimeInstallationTest.java +++ b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/RuntimeInstallationTest.java @@ -50,7 +50,7 @@ public static void main(String[] args) { GlobalConfig.getInstance().loadFrom(root.resolve("app")); CompanionApp.configureLookAndFeel(); SwingUtilities.invokeAndWait(() -> MainWindow.INSTANCE.getEditorTabs().openEditorTab( - new ResourceView(new ArchiveEntrySource(root.resolve("old.jar"), "old.txt", -1), null))); + new ResourceView(MainWindow.INSTANCE.editorContext(), new ArchiveEntrySource(root.resolve("old.jar"), "old.txt", -1), null))); var uiStarted = CompanionApp.class.getDeclaredField("uiStarted"); uiStarted.setAccessible(true); uiStarted.set(null, true); @@ -77,7 +77,7 @@ public static void main(String[] args) { try { install.invoke(null, accepted, bytes); SwingUtilities.invokeAndWait(() -> { - var view = new ResourceView(new ArchiveEntrySource(root.resolve("old.jar"), "old.txt", -1), CompanionApp.currentRuntime()); + var view = new ResourceView(MainWindow.INSTANCE.editorContext(), new ArchiveEntrySource(root.resolve("old.jar"), "old.txt", -1), CompanionApp.currentRuntime()); newView.set(view); MainWindow.INSTANCE.getEditorTabs().openEditorTab(view); }); diff --git a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ScriptPanelDisposalTest.java b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ScriptPanelDisposalTest.java index 54276161..6dfeeff3 100644 --- a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ScriptPanelDisposalTest.java +++ b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ScriptPanelDisposalTest.java @@ -1,5 +1,6 @@ package com.github.minecraft_ta.totalDebugCompanion; +import com.github.minecraft_ta.totalDebugCompanion.ui.views.MainWindow; import com.github.minecraft_ta.totalDebugCompanion.model.ScriptView; import com.github.minecraft_ta.totalDebugCompanion.session.CompanionLaunchConfiguration; import com.github.minecraft_ta.totalDebugCompanion.session.CompanionProfile; @@ -48,13 +49,13 @@ public static void main(String[] args) { CompanionApp.configureWithoutSession(CompanionProfile.forGame(Files.createDirectories(root.resolve("game")))); CompanionApp.configureLookAndFeel(); CompanionApp.configureTokenMakers(); - try (var server = new Server()) { + try (var session = CompanionApp.session()) { + var server = session.server(); var bus = new TrackingBus(); server.setMessageBus(bus); - CompanionApp.SERVER = server; SwingUtilities.invokeAndWait(() -> { - var first = (ScriptPanel) new ScriptView("First").getComponent(); - var second = (ScriptPanel) new ScriptView("Second").getComponent(); + var first = (ScriptPanel) new ScriptView(MainWindow.INSTANCE.editorContext(), "First").getComponent(); + var second = (ScriptPanel) new ScriptView(MainWindow.INSTANCE.editorContext(), "Second").getComponent(); Window firstCompletion = popup(first, "codeCompletionPopup"); Window firstSignature = popup(first, "signatureHelpPopup"); Window secondCompletion = popup(second, "codeCompletionPopup"); @@ -62,15 +63,14 @@ public static void main(String[] args) { firstCompletion.pack(); firstSignature.pack(); secondCompletion.pack(); - assertEquals(Set.of(first, second), bus.owners); + assertEquals(2, bus.owners.size()); try (var replacement = new Server()) { - CompanionApp.SERVER = replacement; first.dispose(); first.dispose(); assertFalse(firstCompletion.isDisplayable()); assertFalse(firstSignature.isDisplayable()); assertTrue(secondCompletion.isDisplayable()); - assertEquals(Set.of(second), bus.owners); + assertEquals(1, bus.owners.size()); second.dispose(); assertTrue(bus.owners.isEmpty()); } @@ -99,6 +99,10 @@ private static final class TrackingBus extends DefaultMessageBus { super.listenAlways(type, owner, listener); owners.add(owner); } + @Override public void unregister(Class type, Consumer listener) { + super.unregister(type, listener); + owners.remove(listener); + } @Override public void unregister(Class type, Object owner) { super.unregister(type, owner); owners.remove(owner); diff --git a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/UiDevHarness.java b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/UiDevHarness.java index 6fbaf945..0d3edd9f 100644 --- a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/UiDevHarness.java +++ b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/UiDevHarness.java @@ -34,6 +34,7 @@ import java.awt.Graphics2D; import java.awt.Point; import java.awt.event.MouseEvent; +import java.awt.event.WindowEvent; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; @@ -41,6 +42,7 @@ import java.nio.file.Path; import java.util.Arrays; import java.util.List; +import java.util.concurrent.TimeUnit; import java.util.Objects; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @@ -260,13 +262,16 @@ private static T findComponent(Container root, Class ty private static void scheduleSearchEverywhereInteractionVerification() { javax.swing.Timer openTimer = new javax.swing.Timer(500, event -> { MainWindow.INSTANCE.openSearchEverywhere(); + SearchEverywherePopup popup = Arrays.stream(java.awt.Window.getWindows()) + .filter(SearchEverywherePopup.class::isInstance) + .map(SearchEverywherePopup.class::cast) + .filter(java.awt.Window::isShowing) + .findFirst() + .orElseThrow(); + // This fixture sends synthetic events; native focus belongs to the user's other windows. + var focusListeners = popup.getWindowFocusListeners(); + for (var listener : focusListeners) popup.removeWindowFocusListener(listener); javax.swing.Timer firstQuery = new javax.swing.Timer(250, queryEvent -> { - SearchEverywherePopup popup = Arrays.stream(java.awt.Window.getWindows()) - .filter(SearchEverywherePopup.class::isInstance) - .map(SearchEverywherePopup.class::cast) - .filter(java.awt.Window::isShowing) - .findFirst() - .orElseThrow(); popup.setLocation(MainWindow.INSTANCE.getX() + 220, MainWindow.INSTANCE.getY() + 70); FlatIconTextField search = findComponent(popup, FlatIconTextField.class); if (search == null) { @@ -274,15 +279,18 @@ private static void scheduleSearchEverywhereInteractionVerification() { } search.setText("Theme"); - javax.swing.Timer verifyResults = new javax.swing.Timer(450, verifyEvent -> { + long resultsDeadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(3); + javax.swing.Timer verifyResults = new javax.swing.Timer(50, verifyEvent -> { @SuppressWarnings("rawtypes") JList results = findComponent(popup, JList.class); if (results == null || results.getModel().getSize() == 0 || !results.isShowing()) { + if (System.nanoTime() < resultsDeadline) return; throw new IllegalStateException("Initial Search Everywhere results did not become visible"); } + ((javax.swing.Timer) verifyEvent.getSource()).stop(); int previousResultCount = results.getModel().getSize(); - search.setText("ThemeS"); + search.setText("ThemeSampleImpl"); if (!results.isShowing() || results.getModel().getSize() != previousResultCount) { throw new IllegalStateException("Typing replaced visible results with a transient blank state"); } @@ -300,19 +308,24 @@ private static void scheduleSearchEverywhereInteractionVerification() { ); } - javax.swing.Timer verifyUpdated = new javax.swing.Timer(450, updatedEvent -> { - if (results.getModel().getSize() == 0 || !results.isShowing()) { + long updatedDeadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(3); + javax.swing.Timer verifyUpdated = new javax.swing.Timer(50, updatedEvent -> { + if (results.getModel().getSize() != 1 || !results.isShowing()) { + if (System.nanoTime() < updatedDeadline) return; throw new IllegalStateException("Updated Search Everywhere results did not remain visible"); } + ((javax.swing.Timer) updatedEvent.getSource()).stop(); + for (var listener : focusListeners) popup.addWindowFocusListener(listener); + var lostFocus = new WindowEvent(popup, WindowEvent.WINDOW_LOST_FOCUS); + for (var listener : focusListeners) listener.windowLostFocus(lostFocus); + if (popup.isVisible()) throw new IllegalStateException("Losing focus must dismiss Search Everywhere"); System.out.println("Search Everywhere interaction verification passed"); popup.dispose(); MainWindow.INSTANCE.dispose(); System.exit(0); }); - verifyUpdated.setRepeats(false); verifyUpdated.start(); }); - verifyResults.setRepeats(false); verifyResults.start(); }); firstQuery.setRepeats(false); @@ -924,7 +937,7 @@ public static void main(String[] args) throws Exception { SwingUtilities.invokeAndWait(() -> { FlatInspector.install("F9"); FlatUIDefaultsInspector.install("F10"); - MainWindow.INSTANCE.getEditorTabs().openEditorTab(new CodeView( + MainWindow.INSTANCE.getEditorTabs().openEditorTab(new CodeView(MainWindow.INSTANCE.editorContext(), decompiledSample, 0, EditorLocation.forRuntimeClass( @@ -932,16 +945,16 @@ public static void main(String[] args) throws Exception { sampleClasses.toUri().toASCIIString() ), CompanionApp.currentRuntime() )); - MainWindow.INSTANCE.getEditorTabs().openEditorTab(new ResourceView( + MainWindow.INSTANCE.getEditorTabs().openEditorTab(new ResourceView(MainWindow.INSTANCE.editorContext(), new ArchiveEntrySource(sampleArchive, "META-INF/MANIFEST.MF", -1), CompanionApp.currentRuntime() )); - MainWindow.INSTANCE.getEditorTabs().openEditorTab(new ResourceView( + MainWindow.INSTANCE.getEditorTabs().openEditorTab(new ResourceView(MainWindow.INSTANCE.editorContext(), new ArchiveEntrySource(sampleArchive, "docs/NOTICE.custom", -1), CompanionApp.currentRuntime() )); - MainWindow.INSTANCE.getEditorTabs().openEditorTab(new ResourceView( + MainWindow.INSTANCE.getEditorTabs().openEditorTab(new ResourceView(MainWindow.INSTANCE.editorContext(), new ArchiveEntrySource(sampleArchive, "config/defaults.toml", -1), CompanionApp.currentRuntime() )); - MainWindow.INSTANCE.getEditorTabs().openEditorTab(new ResourceView( + MainWindow.INSTANCE.getEditorTabs().openEditorTab(new ResourceView(MainWindow.INSTANCE.editorContext(), new ArchiveEntrySource(sampleArchive, "assets/sample/textures/gui/debug.png", -1), CompanionApp.currentRuntime() )); boolean interactionVerification = Arrays.asList(args).stream() @@ -996,7 +1009,7 @@ public static void main(String[] args) throws Exception { MainWindow.INSTANCE.setVisible(true); } else { MainWindow.INSTANCE.setVisible(true); - UIUtils.centerJFrame(MainWindow.INSTANCE); + UIUtils.centerJFrame(MainWindow.INSTANCE, MainWindow.INSTANCE); } ToolTipManager.sharedInstance().setInitialDelay(200); MainWindow.INSTANCE.setRuntimeIndexStatus(new RuntimeIndexService.Status( diff --git a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/UiScenarioDriver.java b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/UiScenarioDriver.java index 122b4063..b47c5e06 100644 --- a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/UiScenarioDriver.java +++ b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/UiScenarioDriver.java @@ -138,7 +138,7 @@ private static void advance(UiRenderScenario scenario, ScenarioContext context) ); case EVALUATE_CODE, EVALUATE_EXPRESSION -> context.once("open-evaluate", () -> { var window = new com.github.minecraft_ta.totalDebugCompanion.ui.views.EvaluateExpressionWindow( - MainWindow.INSTANCE, null); // This fixture renders the editor without an execution backend. + MainWindow.INSTANCE, null, MainWindow.INSTANCE.editorContext(), MainWindow.INSTANCE::refreshRuntimeSources); // This fixture renders the editor without an execution backend. var editor = findComponent(window, com.github.minecraft_ta.totalDebugCompanion.ui.components.JavaExpressionField.class); editor.setText(scenario == UiRenderScenario.EVALUATE_CODE ? "var values = java.util.List.of(1, 2, 3);\nint total = 0;\nfor (int value : values) {\n total += value;\n}\nreturn total;" @@ -157,14 +157,14 @@ private static void advance(UiRenderScenario scenario, ScenarioContext context) case IMPLEMENTATION_CHOOSER -> advanceImplementationChooser(context); case SEARCH_EMPTY, SEARCH_RESULTS, MODULE_FILTER -> advanceSearch(scenario, context); case USAGES_RESULTS -> context.once("open-usages", () -> { - UsagesView view = new UsagesView(new CodeSymbol.ClassSymbol("sample.ThemeSample"), CompanionApp.currentRuntime()); + UsagesView view = new UsagesView(MainWindow.INSTANCE.editorContext(), new CodeSymbol.ClassSymbol("sample.ThemeSample"), CompanionApp.currentRuntime()); MainWindow.INSTANCE.getEditorTabs().openEditorTab(view) .thenRun(() -> SwingUtilities.invokeLater(view::restartSearch)); }); case SETTINGS -> { selectCodeEditor(context); context.once("open-settings", () -> { - SettingsWindow settings = new SettingsWindow(MainWindow.INSTANCE); + SettingsWindow settings = new SettingsWindow(MainWindow.INSTANCE, CompanionApp.instanceState(), CompanionApp.getDebuggerController()); settings.setLocation(MainWindow.INSTANCE.getX() + 250, MainWindow.INSTANCE.getY() + 80); settings.setVisible(true); }); diff --git a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/jdt/diagnostics/CustomJavaLinkGeneratorTest.java b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/jdt/diagnostics/CustomJavaLinkGeneratorTest.java index 4b4fccaf..cf89febb 100644 --- a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/jdt/diagnostics/CustomJavaLinkGeneratorTest.java +++ b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/jdt/diagnostics/CustomJavaLinkGeneratorTest.java @@ -36,6 +36,21 @@ void packageImportSegmentRevealsTheSelectedPackage() { assertEquals("java.util in java.util.List", revealedPackage.get()); } + @Test + void unresolvedPackageOwnerIsPassedToTheDiagnosticRoute() { + String source = "import missing.Type; class Sample {}"; + AtomicReference diagnostic = new AtomicReference<>(); + var generator = new CustomJavaLinkGenerator(offset -> packageFragment("missing"), offset -> null, + (name, owner) -> { + assertNull(owner); + diagnostic.set("Unresolved owner for " + name); + }, Path.of("Sample.java")); + var link = generator.isLinkAtOffset(new RSyntaxTextArea(source), source.indexOf("missing")); + assertNotNull(link); + link.execute(); + assertEquals("Unresolved owner for missing", diagnostic.get()); + } + @Test void linkResultStartsAtTheTokenSoRSyntaxTextAreaCanUnderlineIt() { String source = "final class Sample { java.util.List values; }"; diff --git a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/search/reference/ReferenceSearchServiceTest.java b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/search/reference/ReferenceSearchServiceTest.java index d8663d09..95abf608 100644 --- a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/search/reference/ReferenceSearchServiceTest.java +++ b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/search/reference/ReferenceSearchServiceTest.java @@ -40,7 +40,7 @@ void disposingAnUnattachedUsagePanelCancelsItsSearch() throws Exception { var handle = new AtomicReference(); SwingUtilities.invokeAndWait(() -> { panel.set(new UsagesViewPanel( - new CodeSymbol.ClassSymbol("example.Target"), service)); + new CodeSymbol.ClassSymbol("example.Target"), service, ignored -> {})); panel.get().restartSearch(); try { var field = panel.get().getClass().getDeclaredField("activeSearch"); diff --git a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/session/CompanionSessionRejectionTest.java b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/session/CompanionSessionRejectionTest.java index 41f01572..6f378b5e 100644 --- a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/session/CompanionSessionRejectionTest.java +++ b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/session/CompanionSessionRejectionTest.java @@ -14,6 +14,11 @@ import com.github.minecraft_ta.totaldebug.protocol.execution.ExecutionResult; import com.github.minecraft_ta.totaldebug.protocol.execution.ExecutionText; import org.junit.jupiter.api.Test; +import com.github.minecraft_ta.totalDebugCompanion.project.ProjectScope; +import com.github.minecraft_ta.totalDebugCompanion.storage.InstanceState; +import com.github.minecraft_ta.totalDebugCompanion.script.ScriptCompilationService; +import com.github.minecraft_ta.totalDebugCompanion.script.ScriptExecutionService; +import com.github.minecraft_ta.totaldebug.protocol.execution.ScriptExecutionEnvironment; import org.junit.jupiter.api.io.TempDir; import java.nio.file.Path; import java.util.concurrent.CompletableFuture; @@ -35,7 +40,7 @@ void unauthenticatedResultsNeverReachApplicationListeners() throws Exception { AtomicInteger delivered = new AtomicInteger(); try (CompanionSession session = new CompanionSession(token); Client client = configuredClient(null)) { - session.server().getMessageBus().listenAlways(ExecutionResultMessage.class, + session.addExecutionResultListener( message -> delivered.incrementAndGet()); client.getMessageProcessor().registerMessage(CompanionProtocol.EXECUTION_RESULT, TestExecutionResult.class); session.bindAndPublish(configuration); @@ -149,6 +154,40 @@ void authenticatedHandshakePublishesReadyAfterServerHello() throws Exception { } } + @Test + void scriptAdmissionRejectsUnauthenticatedSocketsAndASwitchRace() throws Exception { + String token = "correct-token-value-1234567890abcdef"; + var configuration = new CompanionLaunchConfiguration(temporaryDirectory); + Object lifecycle = new Object(); + var scope = new ProjectScope(lifecycle, new CompanionProfile("test", temporaryDirectory, temporaryDirectory), InstanceState.inMemory()); + try (var session = new CompanionSession(token); + var compiler = new ScriptCompilationService(message -> true, message -> true); + Client client = configuredClient(null)) { + var scripts = new ScriptExecutionService(session, compiler, session::isConnected); + session.bindAndPublish(configuration); + var response = connect(client, CompanionSessionDescriptor.read(configuration.descriptorFile(), CompanionProtocol.VERSION)); + assertFalse(scripts.run(scope, 1, "source", false, ScriptExecutionEnvironment.THREAD, failure -> {})); + client.getMessageProcessor().enqueueMessage(new TestClientHello(token)); + assertTrue(response.get(2, TimeUnit.SECONDS).accepted); + assertTrue(session.isConnected()); + var result = new CompletableFuture(); + Thread submitter = Thread.ofPlatform().unstarted(() -> { + try { result.complete(scripts.run(scope, 2, "source", false, ScriptExecutionEnvironment.THREAD, failure -> {})); } + catch (Throwable failure) { result.completeExceptionally(failure); } + }); + synchronized (lifecycle) { + submitter.start(); + long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(2); + while (submitter.getState() != Thread.State.BLOCKED && System.nanoTime() < deadline) Thread.sleep(1); + assertEquals(Thread.State.BLOCKED, submitter.getState(), "Submission must be waiting at the scope gate"); + scope.beginSwitch(); + } + assertFalse(result.get(2, TimeUnit.SECONDS), "A gate rejection must preserve the boolean caller contract"); + scope.cancelSwitch(); + assertTrue(scope.isActive()); + } finally { scope.retire(); scope.close(); } + } + private static Client configuredClient(String token) { Client client = new Client(); client.getMessageProcessor().setMaxFrameSize(DefaultMessageProcessor.DEFAULT_MAX_FRAME_SIZE); diff --git a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/global/ApplicationStatusBarTest.java b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/global/ApplicationStatusBarTest.java index 18f015bd..19d1b154 100644 --- a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/global/ApplicationStatusBarTest.java +++ b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/global/ApplicationStatusBarTest.java @@ -30,7 +30,7 @@ class ApplicationStatusBarTest { void usesAThinFixedWidthActivityIndicator() throws Exception { AtomicReference result = new AtomicReference<>(); SwingUtilities.invokeAndWait(() -> { - ApplicationStatusBar bar = new ApplicationStatusBar(target -> {}); + ApplicationStatusBar bar = new ApplicationStatusBar(target -> {}, () -> {}); bar.setRuntimeStatus(new RuntimeIndexService.Status( RuntimeIndexService.Phase.BUILDING, "Building class index", @@ -52,7 +52,7 @@ void usesAThinFixedWidthActivityIndicator() throws Exception { @Test void serviceWidgetsRenderOnlyPublishedState() throws Exception { AtomicReference result = new AtomicReference<>(); - SwingUtilities.invokeAndWait(() -> result.set(new ApplicationStatusBar(target -> {}))); + SwingUtilities.invokeAndWait(() -> result.set(new ApplicationStatusBar(target -> {}, () -> {}))); ApplicationStatusBar bar = result.get(); assertNotNull(findButton(bar, "Game: Offline")); @@ -79,7 +79,7 @@ void serviceWidgetsRenderOnlyPublishedState() throws Exception { void centersContentWithinTheStatusBar() throws Exception { AtomicReference result = new AtomicReference<>(); SwingUtilities.invokeAndWait(() -> { - ApplicationStatusBar bar = new ApplicationStatusBar(target -> {}); + ApplicationStatusBar bar = new ApplicationStatusBar(target -> {}, () -> {}); bar.setRuntimeStatus(new RuntimeIndexService.Status( RuntimeIndexService.Phase.READY, "Runtime index ready", @@ -113,7 +113,7 @@ void breadcrumbButtonsPublishSemanticTargets() throws Exception { List navigated = new ArrayList<>(); AtomicReference result = new AtomicReference<>(); SwingUtilities.invokeAndWait(() -> { - ApplicationStatusBar bar = new ApplicationStatusBar(navigated::add); + ApplicationStatusBar bar = new ApplicationStatusBar(navigated::add, () -> {}); bar.setEditor(new IEditorPanel() { @Override public String getTitle() { return "GrassBlock"; } @Override public String getTooltip() { return "GrassBlock"; } diff --git a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/global/WorkspacePanelTest.java b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/global/WorkspacePanelTest.java index 4d26ec06..d90d9ab8 100644 --- a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/global/WorkspacePanelTest.java +++ b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/components/global/WorkspacePanelTest.java @@ -29,7 +29,7 @@ void workspaceOwnsEveryStructuralRule() throws Exception { AtomicReference result = new AtomicReference<>(); SwingUtilities.invokeAndWait(() -> { FileTreeViewHeader header = new FileTreeViewHeader(); - ApplicationStatusBar statusBar = new ApplicationStatusBar(target -> {}); + ApplicationStatusBar statusBar = new ApplicationStatusBar(target -> {}, () -> {}); assertEquals(0, header.getBorder().getBorderInsets(header).bottom); assertEquals(1, statusBar.getBorder().getBorderInsets(statusBar).top); @@ -65,7 +65,7 @@ void existingWorkspaceRulesFollowThemeChangesAtPaintTime() throws Exception { new FileTreeViewHeader(), new JPanel(), new JPanel(), - new ApplicationStatusBar(target -> {}) + new ApplicationStatusBar(target -> {}, () -> {}) ); workspace.setSize(500, 300); layoutRecursively(workspace); diff --git a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/theme/ThemeLoadingTest.java b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/theme/ThemeLoadingTest.java index b2c8b7b1..b8bd2cff 100644 --- a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/theme/ThemeLoadingTest.java +++ b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/theme/ThemeLoadingTest.java @@ -81,7 +81,7 @@ void themeAppliesTheApplicationDensityContract(CompanionTheme theme) throws Exce tabs.doLayout(); Rectangle tabBounds = tabs.getBoundsAt(0); - ApplicationStatusBar statusBar = new ApplicationStatusBar(target -> {}); + ApplicationStatusBar statusBar = new ApplicationStatusBar(target -> {}, () -> {}); JScrollBar scrollBar = new JScrollBar(); result.set(new RuntimeMetrics( tree.getRowHeight(), diff --git a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/SearchEverywherePopupProcessTest.java b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/SearchEverywherePopupProcessTest.java index eb376c2d..4bed7b84 100644 --- a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/SearchEverywherePopupProcessTest.java +++ b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/SearchEverywherePopupProcessTest.java @@ -1,7 +1,6 @@ package com.github.minecraft_ta.totalDebugCompanion.ui.views; import com.github.minecraft_ta.totalDebugCompanion.UiDevHarness; -import com.github.minecraft_ta.totalDebugCompanion.CompanionApp; import com.github.minecraft_ta.totalDebugCompanion.runtime.RuntimeIndexService; import javax.swing.JLabel; import java.util.Collection; @@ -109,8 +108,9 @@ public static void main(String[] arguments) throws Exception { verifyDisposal(); System.exit(0); } + var service = new RuntimeIndexService(new Object(), snapshot -> snapshot.close()); SwingUtilities.invokeAndWait(() -> { - SearchEverywherePopup popup = new SearchEverywherePopup(); + SearchEverywherePopup popup = new SearchEverywherePopup(null, service, () -> null, target -> {}); try { if (java.util.Arrays.asList(arguments).contains("verify-category-cycling")) { verifyCategoryCycling(popup); @@ -124,18 +124,15 @@ public static void main(String[] arguments) throws Exception { } private static void verifyDisposal() throws Exception { - var serviceField = CompanionApp.class.getDeclaredField("runtimeIndexService"); - serviceField.setAccessible(true); var listenersField = RuntimeIndexService.class.getDeclaredField("listeners"); listenersField.setAccessible(true); var messageField = SearchEverywherePopup.class.getDeclaredField("messageLabel"); messageField.setAccessible(true); try (var service = new RuntimeIndexService(new Object(), snapshot -> snapshot.close())) { - serviceField.set(null, service); for (int cycle = 0; cycle < 3; cycle++) { var label = new AtomicReference(); SwingUtilities.invokeAndWait(() -> { - var popup = new SearchEverywherePopup(); + var popup = new SearchEverywherePopup(null, service, () -> null, target -> {}); try { assertEquals(1, ((Collection) listenersField.get(service)).size()); label.set((JLabel) messageField.get(popup)); @@ -149,7 +146,7 @@ private static void verifyDisposal() throws Exception { service.waiting("sent after disposal"); SwingUtilities.invokeAndWait(() -> assertEquals("unchanged after disposal", label.get().getText())); } - } finally { serviceField.set(null, null); } + } } private static void verifyCategoryCycling(SearchEverywherePopup popup) { diff --git a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerExpressionModelTest.java b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerExpressionModelTest.java index 863903ae..17d514d6 100644 --- a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerExpressionModelTest.java +++ b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerExpressionModelTest.java @@ -1,5 +1,6 @@ package com.github.minecraft_ta.totalDebugCompanion.ui.views.debugger; +import com.github.minecraft_ta.totalDebugCompanion.storage.InstanceState; import com.github.minecraft_ta.totalDebugCompanion.debugger.DebuggerValueLease; import org.junit.jupiter.api.Test; @@ -12,7 +13,7 @@ class DebuggerExpressionModelTest { @Test void replacingAnExpressionReleasesItsValuesAcrossCachedFrames() { - var model = new DebuggerExpressionModel(); + var model = new DebuggerExpressionModel(InstanceState.inMemory()); var released = new AtomicInteger(); model.nextFrame("pause", 1); var key = model.beginExplicit("value"); @@ -28,7 +29,7 @@ void replacingAnExpressionReleasesItsValuesAcrossCachedFrames() { @Test void lateCompletionsReleaseTheirOwnershipAfterRemovalAndPauseExpiry() { - var model = new DebuggerExpressionModel(); + var model = new DebuggerExpressionModel(InstanceState.inMemory()); var released = new AtomicInteger(); model.nextFrame("pause", 1); var key = model.beginExplicit("value"); @@ -45,7 +46,7 @@ void lateCompletionsReleaseTheirOwnershipAfterRemovalAndPauseExpiry() { @Test void anOlderCompletionCannotReplaceANewerRequestForTheSameExpression() { - var model = new DebuggerExpressionModel(); + var model = new DebuggerExpressionModel(InstanceState.inMemory()); var released = new AtomicInteger(); model.nextFrame("pause", 1); var key = model.beginExplicit("value"); diff --git a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerInspectorTest.java b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerInspectorTest.java index 959f9e39..3c994266 100644 --- a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerInspectorTest.java +++ b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerInspectorTest.java @@ -1,5 +1,6 @@ package com.github.minecraft_ta.totalDebugCompanion.ui.views.debugger; +import com.github.minecraft_ta.totalDebugCompanion.storage.InstanceState; import com.github.minecraft_ta.totalDebugCompanion.GlobalConfig; import com.github.minecraft_ta.totalDebugCompanion.debugger.DebugEngine; import com.github.minecraft_ta.totalDebugCompanion.debugger.DebuggerSessionController; @@ -37,16 +38,16 @@ class DebuggerInspectorTest { @Test void evaluatesAutomaticExpressionsOncePerFrameRevision() throws Exception { GlobalConfig config = GlobalConfig.getInstance(); - List previousWatches = com.github.minecraft_ta.totalDebugCompanion.CompanionApp.instanceState().debuggerWatches(); + InstanceState state = InstanceState.inMemory(); boolean previousPreviews = config.automaticDebuggerPreviews(); AtomicInteger inspections = new AtomicInteger(); AtomicInteger explicitEvaluations = new AtomicInteger(); DebuggerSessionController controller = new DebuggerSessionController(ignored -> null); try { - com.github.minecraft_ta.totalDebugCompanion.CompanionApp.instanceState().setDebuggerWatches(List.of("counter()")); + state.setDebuggerWatches(List.of("counter()")); config.setAutomaticDebuggerPreviews(true); SwingUtilities.invokeAndWait(() -> { - DebuggerInspector inspector = new DebuggerInspector( + DebuggerInspector inspector = new DebuggerInspector(state, controller, target -> { }, @@ -74,7 +75,6 @@ void evaluatesAutomaticExpressionsOncePerFrameRevision() throws Exception { inspector.close(); }); } finally { - com.github.minecraft_ta.totalDebugCompanion.CompanionApp.instanceState().setDebuggerWatches(previousWatches); config.setAutomaticDebuggerPreviews(previousPreviews); controller.close(); DebuggerEditorPresentation.clear(); @@ -85,17 +85,15 @@ void evaluatesAutomaticExpressionsOncePerFrameRevision() throws Exception { void publishesOneEditorSnapshotAfterAllRootPreviewsResolve() throws Exception { GlobalConfig config = GlobalConfig.getInstance(); boolean previousPreviews = config.automaticDebuggerPreviews(); - List previousWatches = com.github.minecraft_ta.totalDebugCompanion.CompanionApp.instanceState().debuggerWatches(); DebuggerSessionController controller = new DebuggerSessionController(ignored -> null); AtomicInteger publications = new AtomicInteger(); try { - com.github.minecraft_ta.totalDebugCompanion.CompanionApp.instanceState().setDebuggerWatches(List.of()); config.setAutomaticDebuggerPreviews(true); DebuggerEditorPresentation.clear(); Runnable removeListener = DebuggerEditorPresentation.addListener(snapshot -> publications.incrementAndGet()); publications.set(0); SwingUtilities.invokeAndWait(() -> { - DebuggerInspector inspector = new DebuggerInspector( + DebuggerInspector inspector = new DebuggerInspector(InstanceState.inMemory(), controller, target -> { }, @@ -112,7 +110,6 @@ void publishesOneEditorSnapshotAfterAllRootPreviewsResolve() throws Exception { }); removeListener.run(); } finally { - com.github.minecraft_ta.totalDebugCompanion.CompanionApp.instanceState().setDebuggerWatches(previousWatches); config.setAutomaticDebuggerPreviews(previousPreviews); controller.close(); DebuggerEditorPresentation.clear(); @@ -123,11 +120,9 @@ void publishesOneEditorSnapshotAfterAllRootPreviewsResolve() throws Exception { void retainsExpressionPreviewAcrossUnrelatedTreeRebuilds() throws Exception { GlobalConfig config = GlobalConfig.getInstance(); boolean previousPreviews = config.automaticDebuggerPreviews(); - List previousWatches = com.github.minecraft_ta.totalDebugCompanion.CompanionApp.instanceState().debuggerWatches(); DebuggerSessionController controller = new DebuggerSessionController(ignored -> null); AtomicInteger previewRequests = new AtomicInteger(); try { - com.github.minecraft_ta.totalDebugCompanion.CompanionApp.instanceState().setDebuggerWatches(List.of()); config.setAutomaticDebuggerPreviews(true); SwingUtilities.invokeAndWait(() -> { DebuggerInspector.RuntimeAccess runtime = new DebuggerInspector.RuntimeAccess() { @@ -175,7 +170,7 @@ public CompletableFuture inspect( ); } }; - DebuggerInspector inspector = new DebuggerInspector(controller, target -> { + DebuggerInspector inspector = new DebuggerInspector(InstanceState.inMemory(), controller, target -> { }, runtime); inspector.beginFrame(FRAME); inspector.showVariables(FRAME, List.of()); @@ -192,7 +187,6 @@ public CompletableFuture inspect( inspector.close(); }); } finally { - com.github.minecraft_ta.totalDebugCompanion.CompanionApp.instanceState().setDebuggerWatches(previousWatches); config.setAutomaticDebuggerPreviews(previousPreviews); controller.close(); DebuggerEditorPresentation.clear(); @@ -203,11 +197,9 @@ public CompletableFuture inspect( void loadsLargeChildrenInExplicitBoundedPages() throws Exception { GlobalConfig config = GlobalConfig.getInstance(); boolean previousPreviews = config.automaticDebuggerPreviews(); - List previousWatches = com.github.minecraft_ta.totalDebugCompanion.CompanionApp.instanceState().debuggerWatches(); DebuggerSessionController controller = new DebuggerSessionController(ignored -> null); List starts = new ArrayList<>(); try { - com.github.minecraft_ta.totalDebugCompanion.CompanionApp.instanceState().setDebuggerWatches(List.of()); config.setAutomaticDebuggerPreviews(false); SwingUtilities.invokeAndWait(() -> { DebuggerInspector.RuntimeAccess runtime = new DebuggerInspector.RuntimeAccess() { @@ -254,7 +246,7 @@ public CompletableFuture inspect( return CompletableFuture.failedFuture(new AssertionError("Unexpected inspection")); } }; - DebuggerInspector inspector = new DebuggerInspector(controller, target -> { + DebuggerInspector inspector = new DebuggerInspector(InstanceState.inMemory(), controller, target -> { }, runtime); inspector.beginFrame(FRAME); inspector.showVariables(FRAME, List.of(variable("items", 50, 450))); @@ -278,7 +270,6 @@ public CompletableFuture inspect( inspector.close(); }); } finally { - com.github.minecraft_ta.totalDebugCompanion.CompanionApp.instanceState().setDebuggerWatches(previousWatches); config.setAutomaticDebuggerPreviews(previousPreviews); controller.close(); DebuggerEditorPresentation.clear(); @@ -289,11 +280,9 @@ public CompletableFuture inspect( void loadsNamedObjectFieldsOnceWithoutPagingArguments() throws Exception { GlobalConfig config = GlobalConfig.getInstance(); boolean previousPreviews = config.automaticDebuggerPreviews(); - List previousWatches = com.github.minecraft_ta.totalDebugCompanion.CompanionApp.instanceState().debuggerWatches(); DebuggerSessionController controller = new DebuggerSessionController(ignored -> null); List> requests = new ArrayList<>(); try { - com.github.minecraft_ta.totalDebugCompanion.CompanionApp.instanceState().setDebuggerWatches(List.of()); config.setAutomaticDebuggerPreviews(false); SwingUtilities.invokeAndWait(() -> { DebuggerInspector.RuntimeAccess runtime = new DebuggerInspector.RuntimeAccess() { @@ -335,7 +324,7 @@ public CompletableFuture inspect( return CompletableFuture.failedFuture(new AssertionError("Unexpected inspection")); } }; - DebuggerInspector inspector = new DebuggerInspector(controller, target -> { + DebuggerInspector inspector = new DebuggerInspector(InstanceState.inMemory(), controller, target -> { }, runtime); inspector.beginFrame(FRAME); inspector.showVariables(FRAME, List.of(new DebugEngine.Variable( @@ -353,7 +342,6 @@ public CompletableFuture inspect( inspector.close(); }); } finally { - com.github.minecraft_ta.totalDebugCompanion.CompanionApp.instanceState().setDebuggerWatches(previousWatches); config.setAutomaticDebuggerPreviews(previousPreviews); controller.close(); DebuggerEditorPresentation.clear(); @@ -423,13 +411,11 @@ public CompletableFuture inspect( void replacingAndClosingAnInspectorReleasesValuesIncludingLateResults() throws Exception { GlobalConfig config = GlobalConfig.getInstance(); boolean previousPreviews = config.automaticDebuggerPreviews(); - List previousWatches = com.github.minecraft_ta.totalDebugCompanion.CompanionApp.instanceState().debuggerWatches(); DebuggerSessionController controller = new DebuggerSessionController(ignored -> null); AtomicInteger owners = new AtomicInteger(); AtomicInteger retains = new AtomicInteger(); CompletableFuture late = new CompletableFuture<>(); try { - com.github.minecraft_ta.totalDebugCompanion.CompanionApp.instanceState().setDebuggerWatches(List.of()); config.setAutomaticDebuggerPreviews(false); SwingUtilities.invokeAndWait(() -> { var runtime = runtime(new AtomicInteger(), new AtomicInteger(), List.of(), 77, reference -> { @@ -438,7 +424,7 @@ void replacingAndClosingAnInspectorReleasesValuesIncludingLateResults() throws E owners.incrementAndGet(); return CompletableFuture.completedFuture(owners::decrementAndGet); }); - DebuggerInspector inspector = new DebuggerInspector(controller, ignored -> { }, runtime); + DebuggerInspector inspector = new DebuggerInspector(InstanceState.inMemory(), controller, ignored -> { }, runtime); try { inspector.beginFrame(FRAME); inspector.showVariables(FRAME, List.of()); @@ -462,7 +448,6 @@ void replacingAndClosingAnInspectorReleasesValuesIncludingLateResults() throws E } }); } finally { - com.github.minecraft_ta.totalDebugCompanion.CompanionApp.instanceState().setDebuggerWatches(previousWatches); config.setAutomaticDebuggerPreviews(previousPreviews); controller.close(); DebuggerEditorPresentation.clear(); diff --git a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerPanelTest.java b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerPanelTest.java index 63af3d9d..cb4381d0 100644 --- a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerPanelTest.java +++ b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerPanelTest.java @@ -1,5 +1,6 @@ package com.github.minecraft_ta.totalDebugCompanion.ui.views.debugger; +import com.github.minecraft_ta.totalDebugCompanion.storage.InstanceState; import com.github.minecraft_ta.totalDebugCompanion.debugger.DebuggerSessionController; import com.github.minecraft_ta.totalDebugCompanion.debugger.DebugEngine; import com.github.minecraft_ta.totalDebugCompanion.ui.components.JavaExpressionField; @@ -38,7 +39,7 @@ void exposesExplicitEvaluateAndAddWatchActions() throws Exception { SwingUtilities.invokeAndWait(() -> { DebuggerSessionController controller = new DebuggerSessionController(ignored -> null); DebuggerActions actions = new DebuggerActions(controller); - DebuggerPanel panel = new DebuggerPanel(controller, actions, (frame, activateEditor) -> { + DebuggerPanel panel = new DebuggerPanel(InstanceState.inMemory(), controller, actions, (frame, activateEditor) -> { }); JavaExpressionField field = find(panel, JavaExpressionField.class); @@ -70,7 +71,7 @@ void establishesFrameInspectorSplitOnFirstRealLayout() throws Exception { AtomicReference panelReference = new AtomicReference<>(); DebuggerSessionController controller = new DebuggerSessionController(ignored -> null); DebuggerActions actions = new DebuggerActions(controller); - SwingUtilities.invokeAndWait(() -> panelReference.set(new DebuggerPanel( + SwingUtilities.invokeAndWait(() -> panelReference.set(new DebuggerPanel(InstanceState.inMemory(), controller, actions, (frame, activateEditor) -> { @@ -101,7 +102,7 @@ void hidesDebuggerObjectIdsAndKeepsTheTypeStructured() throws Exception { SwingUtilities.invokeAndWait(() -> { DebuggerSessionController controller = new DebuggerSessionController(ignored -> null); DebuggerActions actions = new DebuggerActions(controller); - DebuggerPanel panel = new DebuggerPanel( + DebuggerPanel panel = new DebuggerPanel(InstanceState.inMemory(), controller, actions, (frame, activateEditor) -> { @@ -151,7 +152,7 @@ void focusesTheDebuggerVariableRequestedByAnEditorHint() throws Exception { SwingUtilities.invokeAndWait(() -> { DebuggerSessionController controller = new DebuggerSessionController(ignored -> null); DebuggerActions actions = new DebuggerActions(controller); - DebuggerPanel panel = new DebuggerPanel(controller, actions, (frame, activateEditor) -> { + DebuggerPanel panel = new DebuggerPanel(InstanceState.inMemory(), controller, actions, (frame, activateEditor) -> { }); DebugEngine.StackFrame frame = new DebugEngine.StackFrame( 1, @@ -196,7 +197,7 @@ void displaysThisBeforeParametersAndLocals() throws Exception { SwingUtilities.invokeAndWait(() -> { DebuggerSessionController controller = new DebuggerSessionController(ignored -> null); DebuggerActions actions = new DebuggerActions(controller); - DebuggerPanel panel = new DebuggerPanel(controller, actions, (frame, activateEditor) -> { + DebuggerPanel panel = new DebuggerPanel(InstanceState.inMemory(), controller, actions, (frame, activateEditor) -> { }); DebugEngine.StackFrame frame = new DebugEngine.StackFrame( 1, @@ -244,7 +245,7 @@ void offersNavigationAssignmentAndCopyActionsForAddressableVariables() throws Ex SwingUtilities.invokeAndWait(() -> { DebuggerSessionController controller = new DebuggerSessionController(ignored -> null); DebuggerActions actions = new DebuggerActions(controller); - DebuggerPanel panel = new DebuggerPanel( + DebuggerPanel panel = new DebuggerPanel(InstanceState.inMemory(), controller, actions, (frame, activateEditor) -> { @@ -299,7 +300,7 @@ void selectingAStackFrameNavigatesItsExactRuntimeClassAndLine() throws Exception SwingUtilities.invokeAndWait(() -> { DebuggerSessionController controller = new DebuggerSessionController(ignored -> null); DebuggerActions actions = new DebuggerActions(controller); - DebuggerPanel panel = new DebuggerPanel( + DebuggerPanel panel = new DebuggerPanel(InstanceState.inMemory(), controller, actions, (frame, activateEditor) -> navigated.set(frame) @@ -344,7 +345,7 @@ void keepsThePreviousPauseVisibleUntilTheNextPauseReplacesItAtomically() throws DebuggerSessionController controller = new DebuggerSessionController(ignored -> null); DebuggerActions actions = new DebuggerActions(controller); AtomicReference navigated = new AtomicReference<>(); - DebuggerPanel panel = new DebuggerPanel( + DebuggerPanel panel = new DebuggerPanel(InstanceState.inMemory(), controller, actions, (frame, activateEditor) -> navigated.set(frame) @@ -426,7 +427,7 @@ void clearsEditorValuesWhileRetainingTheDebuggerSnapshotOnResume() throws Except SwingUtilities.invokeAndWait(() -> { DebuggerSessionController controller = new DebuggerSessionController(ignored -> null); DebuggerActions actions = new DebuggerActions(controller); - DebuggerPanel panel = new DebuggerPanel(controller, actions, (frame, activateEditor) -> { + DebuggerPanel panel = new DebuggerPanel(InstanceState.inMemory(), controller, actions, (frame, activateEditor) -> { }); DebugEngine.StackFrame frame = new DebugEngine.StackFrame( 1, diff --git a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerWindowPreview.java b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerWindowPreview.java index d32e9523..c6205742 100644 --- a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerWindowPreview.java +++ b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerWindowPreview.java @@ -1,5 +1,6 @@ package com.github.minecraft_ta.totalDebugCompanion.ui.views.debugger; +import com.github.minecraft_ta.totalDebugCompanion.storage.InstanceState; import com.github.minecraft_ta.totalDebugCompanion.debugger.DebugEngine; import com.github.minecraft_ta.totalDebugCompanion.debugger.DebugTargetDescriptor; import com.github.minecraft_ta.totalDebugCompanion.debugger.DebuggerSessionController; @@ -19,7 +20,7 @@ public static DebuggerWindow open(Window owner) { DebuggerSessionController controller = new DebuggerSessionController(ignored -> null); DebuggerActions actions = new DebuggerActions(controller); DebuggerShortcuts shortcuts = new DebuggerShortcuts(actions); - DebuggerWindow window = new DebuggerWindow(owner, controller, actions, shortcuts, (frame, activateEditor) -> { + DebuggerWindow window = new DebuggerWindow(InstanceState.inMemory(), owner, controller, actions, shortcuts, (frame, activateEditor) -> { }); window.addWindowListener(new WindowAdapter() { @Override diff --git a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerWindowTest.java b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerWindowTest.java index 4c5e743d..d03e77a2 100644 --- a/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerWindowTest.java +++ b/companion/src/test/java/com/github/minecraft_ta/totalDebugCompanion/ui/views/debugger/DebuggerWindowTest.java @@ -1,5 +1,6 @@ package com.github.minecraft_ta.totalDebugCompanion.ui.views.debugger; +import com.github.minecraft_ta.totalDebugCompanion.storage.InstanceState; import com.github.minecraft_ta.totalDebugCompanion.debugger.DebuggerSessionController; import org.junit.jupiter.api.Test; @@ -29,7 +30,7 @@ void remainsIndependentFromTheSourceWindowStackingOrder() throws Exception { try { SwingUtilities.invokeAndWait(() -> { JFrame sourceWindow = new JFrame("Source"); - DebuggerWindow debuggerWindow = new DebuggerWindow( + DebuggerWindow debuggerWindow = new DebuggerWindow(InstanceState.inMemory(), sourceWindow, controller, actions,