fix: Make runner and debugging async
This commit is contained in:
parent
2e0a507d3f
commit
efbcd55875
11 changed files with 161 additions and 275 deletions
|
@ -17,6 +17,13 @@ Changelog structure reference:
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Runner
|
||||
- The process execution pipeline is now fully asynchronous
|
||||
- Debugger
|
||||
- Zig compilation will no longer cause random IDE freezes
|
||||
|
||||
## [18.1.0]
|
||||
|
||||
### Added
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* Copyright 2023-2024 FalsePattern
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.falsepattern.zigbrains.debugger;
|
||||
|
||||
import com.falsepattern.zigbrains.project.util.CLIUtil;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.val;
|
||||
|
||||
public class Utils {
|
||||
|
||||
public static void executeCommandLineWithErrorChecks(GeneralCommandLine cli) throws ExecutionException, ProcessException {
|
||||
val outputOpt = CLIUtil.execute(cli, Integer.MAX_VALUE);
|
||||
if (outputOpt.isEmpty()) {
|
||||
throw new ExecutionException("Failed to execute \"" + cli.getCommandLineString() + "\"!");
|
||||
}
|
||||
val output = outputOpt.get();
|
||||
if (output.getExitCode() != 0) {
|
||||
throw new ProcessException(cli.getCommandLineString(), output.getStdout(), output.getStderr(), output.getExitCode());
|
||||
}
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public static class ProcessException extends Exception {
|
||||
public final String command;
|
||||
public final String stdout;
|
||||
public final String stderr;
|
||||
public final int exitCode;
|
||||
}
|
||||
}
|
|
@ -16,128 +16,17 @@
|
|||
|
||||
package com.falsepattern.zigbrains.debugger;
|
||||
|
||||
import com.falsepattern.zigbrains.debugger.runner.base.PreLaunchAware;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.filters.Filter;
|
||||
import com.intellij.execution.filters.TextConsoleBuilder;
|
||||
import com.intellij.openapi.util.Expirable;
|
||||
import com.intellij.xdebugger.XDebugSession;
|
||||
import com.jetbrains.cidr.execution.RunParameters;
|
||||
import com.jetbrains.cidr.execution.debugger.CidrDebugProcess;
|
||||
import com.jetbrains.cidr.execution.debugger.CidrLocalDebugProcess;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ZigLocalDebugProcess extends CidrLocalDebugProcess {
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
public enum SuppressionLevel {
|
||||
PreStart,
|
||||
PostStart,
|
||||
Unsuppressed
|
||||
}
|
||||
private volatile SuppressionLevel suppressionLevel;
|
||||
private final List<Runnable> preStart = new ArrayList<>();
|
||||
private final List<Runnable> postStart = new ArrayList<>();
|
||||
public ZigLocalDebugProcess(@NotNull RunParameters parameters, @NotNull XDebugSession session, @NotNull TextConsoleBuilder consoleBuilder)
|
||||
throws ExecutionException {
|
||||
super(parameters, session, consoleBuilder, (project) -> Filter.EMPTY_ARRAY, false);
|
||||
suppressionLevel = parameters instanceof PreLaunchAware ? SuppressionLevel.PreStart : SuppressionLevel.Unsuppressed;
|
||||
}
|
||||
|
||||
public void doStart() {
|
||||
start();
|
||||
lock.lock();
|
||||
try {
|
||||
if (suppressionLevel == SuppressionLevel.PreStart)
|
||||
suppressionLevel = SuppressionLevel.PostStart;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void unSuppress(boolean runPreStart) {
|
||||
lock.lock();
|
||||
try {
|
||||
suppressionLevel = SuppressionLevel.Unsuppressed;
|
||||
if (runPreStart) {
|
||||
for (val r: preStart)
|
||||
r.run();
|
||||
}
|
||||
for (val r: postStart)
|
||||
r.run();
|
||||
preStart.clear();
|
||||
postStart.clear();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private <T> CompletableFuture<T> suppressedFuture(Supplier<CompletableFuture<T>> original) {
|
||||
if (suppressionLevel == SuppressionLevel.Unsuppressed)
|
||||
return original.get();
|
||||
|
||||
lock.lock();
|
||||
try {
|
||||
val level = suppressionLevel;
|
||||
if (level == SuppressionLevel.Unsuppressed)
|
||||
return original.get();
|
||||
|
||||
val bypass = new CompletableFuture<T>();
|
||||
val task = (Runnable)() ->
|
||||
original.get()
|
||||
.thenAccept(bypass::complete)
|
||||
.exceptionally((ex) -> {
|
||||
bypass.completeExceptionally(ex);
|
||||
return null;
|
||||
});
|
||||
switch (level) {
|
||||
case PreStart -> preStart.add(task);
|
||||
case PostStart -> postStart.add(task);
|
||||
}
|
||||
return bypass;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CompletableFuture<Void> postCommand(@NotNull CidrDebugProcess.VoidDebuggerCommand command) {
|
||||
return suppressedFuture(() -> super.postCommand(command));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CompletableFuture<Void> postCommand(
|
||||
@Nullable Expirable expirable, @NotNull CidrDebugProcess.VoidDebuggerCommand command) {
|
||||
return suppressedFuture(() -> super.postCommand(expirable, command));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CompletableFuture<Void> postCommand(
|
||||
@NotNull CidrDebugProcess.VoidDebuggerCommand command, boolean useAlternativeDispatcher) {
|
||||
return suppressedFuture(() -> super.postCommand(command, useAlternativeDispatcher));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull <T> CompletableFuture<T> postCommand(@NotNull CidrDebugProcess.DebuggerCommand<T> command) {
|
||||
return suppressedFuture(() -> super.postCommand(command));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull <T> CompletableFuture<T> postCommand(
|
||||
@Nullable Expirable expirable, @NotNull CidrDebugProcess.DebuggerCommand<T> command) {
|
||||
return suppressedFuture(() -> super.postCommand(expirable, command));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull <T> CompletableFuture<T> postCommand(
|
||||
@NotNull CidrDebugProcess.DebuggerCommand<T> command, boolean useAlternativeDispatcher) {
|
||||
return suppressedFuture(() -> super.postCommand(command, useAlternativeDispatcher));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package com.falsepattern.zigbrains.debugger.runner.base;
|
||||
|
||||
import com.intellij.execution.ExecutionException;
|
||||
|
||||
public interface PreLaunchAware {
|
||||
void preLaunch() throws Exception;
|
||||
void preLaunch(PreLaunchProcessListener listener) throws ExecutionException;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package com.falsepattern.zigbrains.debugger.runner.base;
|
||||
|
||||
import com.falsepattern.zigbrains.project.runconfig.ZigProcessHandler;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.intellij.execution.process.ProcessEvent;
|
||||
import com.intellij.execution.process.ProcessHandler;
|
||||
import com.intellij.execution.process.ProcessListener;
|
||||
import com.intellij.execution.ui.ConsoleView;
|
||||
import com.intellij.execution.ui.ConsoleViewContentType;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public class PreLaunchProcessListener implements ProcessListener {
|
||||
private final ConsoleView console;
|
||||
private boolean buildFailed = false;
|
||||
private ProcessHandler processHandler;
|
||||
|
||||
public boolean executeCommandLineWithHook(GeneralCommandLine commandLine) throws ExecutionException {
|
||||
processHandler = new ZigProcessHandler(commandLine);
|
||||
hook(processHandler);
|
||||
processHandler.startNotify();
|
||||
processHandler.waitFor();
|
||||
return buildFailed;
|
||||
}
|
||||
|
||||
public void hook(ProcessHandler handler) {
|
||||
console.attachToProcess(handler);
|
||||
handler.addProcessListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processTerminated(@NotNull ProcessEvent event) {
|
||||
if (event.getExitCode() != 0) {
|
||||
console.print("Process finished with exit code " + event.getExitCode(),
|
||||
ConsoleViewContentType.NORMAL_OUTPUT);
|
||||
buildFailed = true;
|
||||
} else {
|
||||
buildFailed = false;
|
||||
console.print("Build Successful. Starting debug session. \n", ConsoleViewContentType.NORMAL_OUTPUT);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package com.falsepattern.zigbrains.debugger.runner.base;
|
||||
|
||||
import com.falsepattern.zigbrains.debugger.Utils;
|
||||
import com.falsepattern.zigbrains.project.execution.base.ProfileStateBase;
|
||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
|
@ -37,8 +36,8 @@ public abstract class ZigDebugParametersEmitBinaryBase<ProfileState extends Prof
|
|||
this.kind = kind;
|
||||
}
|
||||
|
||||
private File compileExe()
|
||||
throws ExecutionException, Utils.ProcessException {
|
||||
private File compileExe(PreLaunchProcessListener listener)
|
||||
throws ExecutionException {
|
||||
final File executableFile;
|
||||
val commandLine = profileState.getCommandLine(toolchain, true);
|
||||
final Path tmpDir;
|
||||
|
@ -49,7 +48,10 @@ public abstract class ZigDebugParametersEmitBinaryBase<ProfileState extends Prof
|
|||
}
|
||||
val exe = tmpDir.resolve("executable").toFile();
|
||||
commandLine.addParameters("-femit-bin=" + exe.getAbsolutePath());
|
||||
Utils.executeCommandLineWithErrorChecks(commandLine);
|
||||
|
||||
if (listener.executeCommandLineWithHook(commandLine))
|
||||
return null;
|
||||
|
||||
//Find our binary
|
||||
try (val stream = Files.list(tmpDir)){
|
||||
executableFile = stream.filter(file -> !file.getFileName().toString().endsWith(".o"))
|
||||
|
@ -65,8 +67,8 @@ public abstract class ZigDebugParametersEmitBinaryBase<ProfileState extends Prof
|
|||
}
|
||||
|
||||
@Override
|
||||
public void preLaunch() throws Exception {
|
||||
this.executableFile = compileExe();
|
||||
public void preLaunch(PreLaunchProcessListener listener) throws ExecutionException {
|
||||
this.executableFile = compileExe(listener);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,13 +16,12 @@
|
|||
|
||||
package com.falsepattern.zigbrains.debugger.runner.base;
|
||||
|
||||
import com.falsepattern.zigbrains.common.util.ApplicationUtil;
|
||||
import com.falsepattern.zigbrains.debugbridge.ZigDebuggerDriverConfigurationProvider;
|
||||
import com.falsepattern.zigbrains.debugger.Utils;
|
||||
import com.falsepattern.zigbrains.debugger.ZigLocalDebugProcess;
|
||||
import com.falsepattern.zigbrains.project.execution.base.ProfileStateBase;
|
||||
import com.falsepattern.zigbrains.project.runconfig.ZigProgramRunnerBase;
|
||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||
import com.intellij.execution.DefaultExecutionResult;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.RunProfile;
|
||||
import com.intellij.execution.executors.DefaultDebugExecutor;
|
||||
|
@ -30,40 +29,31 @@ import com.intellij.execution.filters.Filter;
|
|||
import com.intellij.execution.filters.TextConsoleBuilder;
|
||||
import com.intellij.execution.process.ProcessTerminatedListener;
|
||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||
import com.intellij.execution.runners.RunContentBuilder;
|
||||
import com.intellij.execution.ui.ConsoleView;
|
||||
import com.intellij.execution.ui.ConsoleViewContentType;
|
||||
import com.intellij.execution.ui.RunContentDescriptor;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.xdebugger.XDebugProcess;
|
||||
import com.intellij.xdebugger.XDebugProcessStarter;
|
||||
import com.intellij.xdebugger.XDebugSession;
|
||||
import com.intellij.xdebugger.XDebuggerManager;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriver;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.jetbrains.concurrency.AsyncPromise;
|
||||
|
||||
public abstract class ZigDebugRunnerBase<ProfileState extends ProfileStateBase<?>> extends ZigProgramRunnerBase<ProfileState> {
|
||||
public ZigDebugRunnerBase() {
|
||||
super(DefaultDebugExecutor.EXECUTOR_ID);
|
||||
}
|
||||
|
||||
private static RunContentDescriptor startSession(ExecutionEnvironment environment, XDebugProcessStarter starter)
|
||||
throws ExecutionException {
|
||||
return XDebuggerManager.getInstance(environment.getProject())
|
||||
.startSession(environment, starter)
|
||||
.getRunContentDescriptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RunContentDescriptor doExecute(ProfileState state, AbstractZigToolchain toolchain, ExecutionEnvironment environment)
|
||||
protected void doExecuteAsync(ProfileState state,
|
||||
AbstractZigToolchain toolchain,
|
||||
ExecutionEnvironment environment,
|
||||
AsyncPromise<@Nullable RunContentDescriptor> runContentDescriptorPromise)
|
||||
throws ExecutionException {
|
||||
val project = environment.getProject();
|
||||
val drivers = ZigDebuggerDriverConfigurationProvider.findDebuggerConfigurations(project, false, false)
|
||||
|
@ -76,9 +66,43 @@ public abstract class ZigDebugRunnerBase<ProfileState extends ProfileStateBase<?
|
|||
if (runParameters == null) {
|
||||
continue;
|
||||
}
|
||||
return startSession(environment, new ZigLocalDebugProcessStarter(runParameters, state, environment));
|
||||
val console = state.getConsoleBuilder().getConsole();
|
||||
if (runParameters instanceof PreLaunchAware pla) {
|
||||
val listener = new PreLaunchProcessListener(console);
|
||||
pla.preLaunch(listener);
|
||||
if (listener.isBuildFailed()) {
|
||||
val executionResult = new DefaultExecutionResult(console, listener.getProcessHandler());
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
val runContentBuilder = new RunContentBuilder(executionResult, environment);
|
||||
val runContentDescriptor = runContentBuilder.showRunContent(null);
|
||||
runContentDescriptorPromise.setResult(runContentDescriptor);
|
||||
});
|
||||
return;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
val debuggerManager = XDebuggerManager.getInstance(environment.getProject());
|
||||
try {
|
||||
val xDebugSession = debuggerManager.startSession(environment, new XDebugProcessStarter() {
|
||||
@Override
|
||||
public @NotNull XDebugProcess start(@NotNull XDebugSession session)
|
||||
throws ExecutionException {
|
||||
val project = session.getProject();
|
||||
val textConsoleBuilder = new SharedConsoleBuilder(console);
|
||||
val debugProcess = new ZigLocalDebugProcess(runParameters, session, textConsoleBuilder);
|
||||
ProcessTerminatedListener.attach(debugProcess.getProcessHandler(), project);
|
||||
debugProcess.start();
|
||||
return debugProcess;
|
||||
}
|
||||
});
|
||||
runContentDescriptorPromise.setResult(xDebugSession.getRunContentDescriptor());
|
||||
} catch (ExecutionException e) {
|
||||
runContentDescriptorPromise.setError(e);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
runContentDescriptorPromise.setResult(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -87,79 +111,21 @@ public abstract class ZigDebugRunnerBase<ProfileState extends ProfileStateBase<?
|
|||
protected abstract @Nullable ZigDebugParametersBase<ProfileState> getDebugParameters(ProfileState state, ExecutionEnvironment environment, DebuggerDriverConfiguration debuggerDriver, AbstractZigToolchain toolchain) throws ExecutionException;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
private class ZigLocalDebugProcessStarter extends XDebugProcessStarter {
|
||||
private final ZigDebugParametersBase<ProfileState> params;
|
||||
private final ProfileState state;
|
||||
private final ExecutionEnvironment environment;
|
||||
private static class Carrier {
|
||||
volatile ConsoleView console;
|
||||
final Map<ConsoleViewContentType, List<String>> outputs = new HashMap<>();
|
||||
|
||||
void handleOutput(String text, ConsoleViewContentType type) {
|
||||
if (console != null) {
|
||||
console.print(text, type);
|
||||
} else {
|
||||
outputs.computeIfAbsent(type, (ignored) -> new ArrayList<>()).add(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull XDebugProcess start(@NotNull XDebugSession session) throws ExecutionException {
|
||||
val cb = state.getConsoleBuilder();
|
||||
val carrier = new Carrier();
|
||||
val wrappedBuilder = new TextConsoleBuilder() {
|
||||
private static class SharedConsoleBuilder extends TextConsoleBuilder {
|
||||
private final ConsoleView console;
|
||||
@Override
|
||||
public @NotNull ConsoleView getConsole() {
|
||||
val console = cb.getConsole();
|
||||
for (val output: carrier.outputs.entrySet()) {
|
||||
for (val line: output.getValue()) {
|
||||
console.print(line + "\n", output.getKey());
|
||||
}
|
||||
}
|
||||
carrier.console = console;
|
||||
return console;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addFilter(@NotNull Filter filter) {
|
||||
cb.addFilter(filter);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setViewer(boolean isViewer) {
|
||||
cb.setViewer(isViewer);
|
||||
}
|
||||
};
|
||||
val process = new ZigLocalDebugProcess(params, session, wrappedBuilder);
|
||||
if (params instanceof PreLaunchAware pla) {
|
||||
ApplicationManager.getApplication().executeOnPooledThread(() -> {
|
||||
ProcessTerminatedListener.attach(process.getProcessHandler(), environment.getProject());
|
||||
try {
|
||||
pla.preLaunch();
|
||||
} catch (Exception e) {
|
||||
ApplicationUtil.invokeLater(() -> {
|
||||
if (e instanceof Utils.ProcessException pe) {
|
||||
carrier.handleOutput(pe.command + "\n", ConsoleViewContentType.SYSTEM_OUTPUT);
|
||||
carrier.handleOutput("Compilation failure!\n", ConsoleViewContentType.SYSTEM_OUTPUT);
|
||||
carrier.handleOutput(pe.stdout, ConsoleViewContentType.NORMAL_OUTPUT);
|
||||
carrier.handleOutput(pe.stderr, ConsoleViewContentType.ERROR_OUTPUT);
|
||||
process.handleTargetTerminated(new DebuggerDriver.ExitStatus(pe.exitCode));
|
||||
} else {
|
||||
carrier.handleOutput("Exception while compiling binary:\n", ConsoleViewContentType.SYSTEM_OUTPUT);
|
||||
carrier.handleOutput(e.getMessage(), ConsoleViewContentType.ERROR_OUTPUT);
|
||||
process.handleTargetTerminated(new DebuggerDriver.ExitStatus(-1));
|
||||
}
|
||||
process.stop();
|
||||
ApplicationManager.getApplication().executeOnPooledThread(() -> process.unSuppress(false));
|
||||
});
|
||||
return;
|
||||
}
|
||||
process.unSuppress(true);
|
||||
});
|
||||
}
|
||||
process.doStart();
|
||||
return process;
|
||||
public void setViewer(boolean b) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
|
||||
package com.falsepattern.zigbrains.debugger.runner.build;
|
||||
|
||||
import com.falsepattern.zigbrains.debugger.Utils;
|
||||
import com.falsepattern.zigbrains.debugger.runner.base.PreLaunchAware;
|
||||
import com.falsepattern.zigbrains.debugger.runner.base.PreLaunchProcessListener;
|
||||
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugEmitBinaryInstaller;
|
||||
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugParametersBase;
|
||||
import com.falsepattern.zigbrains.project.execution.build.ProfileStateBuild;
|
||||
|
@ -33,7 +33,6 @@ import java.io.File;
|
|||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class ZigDebugParametersBuild extends ZigDebugParametersBase<ProfileStateBuild> implements PreLaunchAware {
|
||||
|
@ -46,9 +45,10 @@ public class ZigDebugParametersBuild extends ZigDebugParametersBase<ProfileState
|
|||
|
||||
}
|
||||
|
||||
private File compileExe() throws ExecutionException, Utils.ProcessException {
|
||||
private File compileExe(PreLaunchProcessListener listener) throws ExecutionException {
|
||||
val commandLine = profileState.getCommandLine(toolchain, true);
|
||||
Utils.executeCommandLineWithErrorChecks(commandLine);
|
||||
if (listener.executeCommandLineWithHook(commandLine))
|
||||
return null;
|
||||
val cfg = profileState.configuration();
|
||||
val workingDir = cfg.getWorkingDirectory().getPath().orElse(null);
|
||||
val exePath = profileState.configuration().getExePath().getPath();
|
||||
|
@ -96,8 +96,8 @@ public class ZigDebugParametersBuild extends ZigDebugParametersBase<ProfileState
|
|||
}
|
||||
|
||||
@Override
|
||||
public void preLaunch() throws Exception {
|
||||
this.executableFile = compileExe();
|
||||
public void preLaunch(PreLaunchProcessListener listener) throws ExecutionException {
|
||||
this.executableFile = compileExe(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -52,7 +52,7 @@ public class ZigExecConfigRun extends ZigExecConfigBase<ZigExecConfigRun> {
|
|||
@Override
|
||||
public List<String> buildCommandLineArgs(boolean debug) {
|
||||
val result = new ArrayList<String>();
|
||||
result.add("run");
|
||||
result.add(debug ? "build-exe" : "run");
|
||||
result.addAll(CLIUtil.colored(colored.value, debug));
|
||||
result.add(filePath.getPathOrThrow().toString());
|
||||
if (!debug || optimization.forced) {
|
||||
|
|
|
@ -22,15 +22,20 @@ import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
|||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.RunProfileState;
|
||||
import com.intellij.execution.configurations.RunnerSettings;
|
||||
import com.intellij.execution.runners.AsyncProgramRunner;
|
||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||
import com.intellij.execution.runners.GenericProgramRunner;
|
||||
import com.intellij.execution.ui.RunContentDescriptor;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.util.concurrency.AppExecutorUtil;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.concurrency.AsyncPromise;
|
||||
import org.jetbrains.concurrency.Promise;
|
||||
import org.jetbrains.concurrency.Promises;
|
||||
|
||||
public abstract class ZigProgramRunnerBase<ProfileState extends ProfileStateBase<?>> extends GenericProgramRunner<RunnerSettings> {
|
||||
public abstract class ZigProgramRunnerBase<ProfileState extends ProfileStateBase<?>> extends
|
||||
AsyncProgramRunner<RunnerSettings> {
|
||||
protected final String executorId;
|
||||
|
||||
public ZigProgramRunnerBase(String executorId) {
|
||||
|
@ -38,33 +43,36 @@ public abstract class ZigProgramRunnerBase<ProfileState extends ProfileStateBase
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void execute(@NotNull ExecutionEnvironment environment, @NotNull RunProfileState state) {
|
||||
super.execute(environment, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable RunContentDescriptor doExecute(@NotNull RunProfileState state$, @NotNull ExecutionEnvironment environment)
|
||||
throws ExecutionException {
|
||||
protected Promise<@Nullable RunContentDescriptor> execute(@NotNull ExecutionEnvironment environment, @NotNull RunProfileState state$) {
|
||||
if (!(state$ instanceof ProfileStateBase<?> state$$)) {
|
||||
return null;
|
||||
return Promises.resolvedPromise();
|
||||
}
|
||||
val state = castProfileState(state$$);
|
||||
if (state == null)
|
||||
return null;
|
||||
return Promises.resolvedPromise();
|
||||
|
||||
val toolchain = ZigProjectSettingsService.getInstance(environment.getProject()).getState().getToolchain();
|
||||
if (toolchain == null) {
|
||||
return null;
|
||||
return Promises.resolvedPromise();
|
||||
}
|
||||
|
||||
FileDocumentManager.getInstance().saveAllDocuments();
|
||||
|
||||
return doExecute(state, toolchain, environment);
|
||||
val runContentDescriptorPromise = new AsyncPromise<RunContentDescriptor>();
|
||||
AppExecutorUtil.getAppExecutorService().execute(() -> {
|
||||
try {
|
||||
doExecuteAsync(state, toolchain, environment, runContentDescriptorPromise);
|
||||
} catch (ExecutionException e) {
|
||||
runContentDescriptorPromise.setError(e);
|
||||
}
|
||||
});
|
||||
return runContentDescriptorPromise;
|
||||
}
|
||||
|
||||
protected abstract @Nullable ProfileState castProfileState(ProfileStateBase<?> state);
|
||||
|
||||
protected abstract @Nullable RunContentDescriptor doExecute(ProfileState state,
|
||||
protected abstract void doExecuteAsync(ProfileState state,
|
||||
AbstractZigToolchain toolchain,
|
||||
ExecutionEnvironment environment) throws ExecutionException;
|
||||
ExecutionEnvironment environment,
|
||||
AsyncPromise<@Nullable RunContentDescriptor> runContentDescriptorPromise) throws ExecutionException;
|
||||
}
|
||||
|
|
|
@ -25,9 +25,11 @@ import com.intellij.execution.executors.DefaultRunExecutor;
|
|||
import com.intellij.execution.runners.DefaultProgramRunnerKt;
|
||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||
import com.intellij.execution.ui.RunContentDescriptor;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.concurrency.AsyncPromise;
|
||||
|
||||
public class ZigRegularRunner extends ZigProgramRunnerBase<ProfileStateBase<?>> {
|
||||
public ZigRegularRunner() {
|
||||
|
@ -50,8 +52,17 @@ public class ZigRegularRunner extends ZigProgramRunnerBase<ProfileStateBase<?>>
|
|||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable RunContentDescriptor doExecute(ProfileStateBase<?> state, AbstractZigToolchain toolchain, ExecutionEnvironment environment)
|
||||
protected void doExecuteAsync(ProfileStateBase<?> state,
|
||||
AbstractZigToolchain toolchain,
|
||||
ExecutionEnvironment environment,
|
||||
AsyncPromise<RunContentDescriptor> runContentDescriptorPromise)
|
||||
throws ExecutionException {
|
||||
return DefaultProgramRunnerKt.showRunContent(state.executeCommandLine(state.getCommandLine(toolchain, false), environment), environment);
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
try {
|
||||
runContentDescriptorPromise.setResult(DefaultProgramRunnerKt.showRunContent(state.executeCommandLine(state.getCommandLine(toolchain, false), environment), environment));
|
||||
} catch (ExecutionException e) {
|
||||
runContentDescriptorPromise.setError(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue