backport: 19.0.0

This commit is contained in:
FalsePattern 2024-10-25 10:23:42 +02:00
parent 6a63a2bd16
commit 911bb98860
Signed by: falsepattern
GPG key ID: E930CDEC50C50E23
15 changed files with 221 additions and 289 deletions

View file

@ -17,17 +17,28 @@ Changelog structure reference:
## [Unreleased] ## [Unreleased]
## [18.1.0] ## [19.0.0]
### Added ### Added
- Zig - Zig
- Basic language injections in strings - Basic language injections in strings
### Changed
- Runner
- The process execution pipeline is now fully asynchronous
- Error output is no longer redirected to standard output
### Fixed ### Fixed
- Debugger
- Zig compilation will no longer cause IDE freezes
- Debugging with GDB no longer causes internal IDE warnings
- Debugging `zig run` configurations is now possible
- LSP - LSP
- No more error spam when zig or zls binary is missing. - Rare error when checking LSP presence
- No more error spam when zig or zls binary is missing
## [18.0.0] ## [18.0.0]

View file

@ -11,7 +11,7 @@ baseIDE=clion
ideaVersion=2023.2.8 ideaVersion=2023.2.8
clionVersion=2023.2.5 clionVersion=2023.2.5
pluginVersion=18.1.0 pluginVersion=19.0.0
# Gradle Releases -> https://github.com/gradle/gradle/releases # Gradle Releases -> https://github.com/gradle/gradle/releases
gradleVersion=8.10.2 gradleVersion=8.10.2

View file

@ -31,12 +31,11 @@ public interface ZigDebuggerDriverConfigurationProvider {
ExtensionPointName<ZigDebuggerDriverConfigurationProvider> EXTENSION_POINT_NAME = ExtensionPointName.create("com.falsepattern.zigbrains.debuggerDriverProvider"); ExtensionPointName<ZigDebuggerDriverConfigurationProvider> EXTENSION_POINT_NAME = ExtensionPointName.create("com.falsepattern.zigbrains.debuggerDriverProvider");
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
static @NotNull Stream<DebuggerDriverConfiguration> findDebuggerConfigurations(Project project, boolean isElevated, boolean emulateTerminal) { static @NotNull Stream<Supplier<DebuggerDriverConfiguration>> findDebuggerConfigurations(Project project, boolean isElevated, boolean emulateTerminal) {
return (Stream<DebuggerDriverConfiguration>) EXTENSION_POINT_NAME.getExtensionList() return EXTENSION_POINT_NAME.getExtensionList()
.stream() .stream()
.map(it -> it.getDebuggerConfiguration(project, isElevated, emulateTerminal)) .map(it -> it.getDebuggerConfiguration(project, isElevated, emulateTerminal))
.filter(Objects::nonNull) .filter(Objects::nonNull);
.map((Function<? super Supplier<DebuggerDriverConfiguration>, ?>) Supplier::get);
} }
@Nullable Supplier<DebuggerDriverConfiguration> getDebuggerConfiguration(Project project, boolean isElevated, boolean emulateTerminal); @Nullable Supplier<DebuggerDriverConfiguration> getDebuggerConfiguration(Project project, boolean isElevated, boolean emulateTerminal);

View file

@ -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;
}
}

View file

@ -16,116 +16,17 @@
package com.falsepattern.zigbrains.debugger; package com.falsepattern.zigbrains.debugger;
import com.falsepattern.zigbrains.debugger.runner.base.PreLaunchAware;
import com.intellij.execution.ExecutionException; import com.intellij.execution.ExecutionException;
import com.intellij.execution.filters.Filter; import com.intellij.execution.filters.Filter;
import com.intellij.execution.filters.TextConsoleBuilder; import com.intellij.execution.filters.TextConsoleBuilder;
import com.intellij.openapi.util.Expirable;
import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebugSession;
import com.jetbrains.cidr.execution.RunParameters; import com.jetbrains.cidr.execution.RunParameters;
import com.jetbrains.cidr.execution.debugger.CidrDebugProcess;
import com.jetbrains.cidr.execution.debugger.CidrLocalDebugProcess; import com.jetbrains.cidr.execution.debugger.CidrLocalDebugProcess;
import lombok.val;
import org.jetbrains.annotations.NotNull; 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 { 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) public ZigLocalDebugProcess(@NotNull RunParameters parameters, @NotNull XDebugSession session, @NotNull TextConsoleBuilder consoleBuilder)
throws ExecutionException { throws ExecutionException {
super(parameters, session, consoleBuilder, (project) -> Filter.EMPTY_ARRAY, false); 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(
@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(
@NotNull CidrDebugProcess.DebuggerCommand<T> command, boolean useAlternativeDispatcher) {
return suppressedFuture(() -> super.postCommand(command, useAlternativeDispatcher));
} }
} }

View file

@ -16,6 +16,8 @@
package com.falsepattern.zigbrains.debugger.runner.base; package com.falsepattern.zigbrains.debugger.runner.base;
import com.intellij.execution.ExecutionException;
public interface PreLaunchAware { public interface PreLaunchAware {
void preLaunch() throws Exception; void preLaunch(PreLaunchProcessListener listener) throws ExecutionException;
} }

View file

@ -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);
}
}
}

View file

@ -16,7 +16,6 @@
package com.falsepattern.zigbrains.debugger.runner.base; 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.execution.base.ProfileStateBase;
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain; import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
import com.intellij.execution.ExecutionException; import com.intellij.execution.ExecutionException;
@ -37,8 +36,8 @@ public abstract class ZigDebugParametersEmitBinaryBase<ProfileState extends Prof
this.kind = kind; this.kind = kind;
} }
private File compileExe() private File compileExe(PreLaunchProcessListener listener)
throws ExecutionException, Utils.ProcessException { throws ExecutionException {
final File executableFile; final File executableFile;
val commandLine = profileState.getCommandLine(toolchain, true); val commandLine = profileState.getCommandLine(toolchain, true);
final Path tmpDir; final Path tmpDir;
@ -49,7 +48,10 @@ public abstract class ZigDebugParametersEmitBinaryBase<ProfileState extends Prof
} }
val exe = tmpDir.resolve("executable").toFile(); val exe = tmpDir.resolve("executable").toFile();
commandLine.addParameters("-femit-bin=" + exe.getAbsolutePath()); commandLine.addParameters("-femit-bin=" + exe.getAbsolutePath());
Utils.executeCommandLineWithErrorChecks(commandLine);
if (listener.executeCommandLineWithHook(commandLine))
return null;
//Find our binary //Find our binary
try (val stream = Files.list(tmpDir)){ try (val stream = Files.list(tmpDir)){
executableFile = stream.filter(file -> !file.getFileName().toString().endsWith(".o")) executableFile = stream.filter(file -> !file.getFileName().toString().endsWith(".o"))
@ -65,8 +67,8 @@ public abstract class ZigDebugParametersEmitBinaryBase<ProfileState extends Prof
} }
@Override @Override
public void preLaunch() throws Exception { public void preLaunch(PreLaunchProcessListener listener) throws ExecutionException {
this.executableFile = compileExe(); this.executableFile = compileExe(listener);
} }
} }

View file

@ -16,13 +16,12 @@
package com.falsepattern.zigbrains.debugger.runner.base; package com.falsepattern.zigbrains.debugger.runner.base;
import com.falsepattern.zigbrains.common.util.ApplicationUtil;
import com.falsepattern.zigbrains.debugbridge.ZigDebuggerDriverConfigurationProvider; import com.falsepattern.zigbrains.debugbridge.ZigDebuggerDriverConfigurationProvider;
import com.falsepattern.zigbrains.debugger.Utils;
import com.falsepattern.zigbrains.debugger.ZigLocalDebugProcess; import com.falsepattern.zigbrains.debugger.ZigLocalDebugProcess;
import com.falsepattern.zigbrains.project.execution.base.ProfileStateBase; import com.falsepattern.zigbrains.project.execution.base.ProfileStateBase;
import com.falsepattern.zigbrains.project.runconfig.ZigProgramRunnerBase; import com.falsepattern.zigbrains.project.runconfig.ZigProgramRunnerBase;
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain; import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
import com.intellij.execution.DefaultExecutionResult;
import com.intellij.execution.ExecutionException; import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.RunProfile; import com.intellij.execution.configurations.RunProfile;
import com.intellij.execution.executors.DefaultDebugExecutor; import com.intellij.execution.executors.DefaultDebugExecutor;
@ -30,55 +29,112 @@ import com.intellij.execution.filters.Filter;
import com.intellij.execution.filters.TextConsoleBuilder; import com.intellij.execution.filters.TextConsoleBuilder;
import com.intellij.execution.process.ProcessTerminatedListener; import com.intellij.execution.process.ProcessTerminatedListener;
import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.runners.RunContentBuilder;
import com.intellij.execution.ui.ConsoleView; import com.intellij.execution.ui.ConsoleView;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.execution.ui.RunContentDescriptor; import com.intellij.execution.ui.RunContentDescriptor;
import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ApplicationManager;
import com.intellij.util.concurrency.AppExecutorUtil;
import com.intellij.xdebugger.XDebugProcess; import com.intellij.xdebugger.XDebugProcess;
import com.intellij.xdebugger.XDebugProcessStarter; import com.intellij.xdebugger.XDebugProcessStarter;
import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebugSession;
import com.intellij.xdebugger.XDebuggerManager; import com.intellij.xdebugger.XDebuggerManager;
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriver;
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration; import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.val; import lombok.val;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.concurrency.AsyncPromise;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.function.Supplier;
import java.util.stream.Collectors;
public abstract class ZigDebugRunnerBase<ProfileState extends ProfileStateBase<?>> extends ZigProgramRunnerBase<ProfileState> { public abstract class ZigDebugRunnerBase<ProfileState extends ProfileStateBase<?>> extends ZigProgramRunnerBase<ProfileState> {
public ZigDebugRunnerBase() { public ZigDebugRunnerBase() {
super(DefaultDebugExecutor.EXECUTOR_ID); super(DefaultDebugExecutor.EXECUTOR_ID);
} }
private static RunContentDescriptor startSession(ExecutionEnvironment environment, XDebugProcessStarter starter) private boolean doExecuteAsyncWithDriver(ProfileState state,
AbstractZigToolchain toolchain,
ExecutionEnvironment environment,
AsyncPromise<RunContentDescriptor> runContentDescriptorPromise,
DebuggerDriverConfiguration debuggerDriver) throws ExecutionException {
ZigDebugParametersBase<ProfileState> runParameters = getDebugParameters(state, environment, debuggerDriver, toolchain);
if (runParameters == null) {
return false;
}
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 true;
}
}
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 { throws ExecutionException {
return XDebuggerManager.getInstance(environment.getProject()) val project = session.getProject();
.startSession(environment, starter) val textConsoleBuilder = new SharedConsoleBuilder(console);
.getRunContentDescriptor(); 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 true;
}
private void doExecuteAsyncFetchNextDriver(ProfileState state,
AbstractZigToolchain toolchain,
ExecutionEnvironment environment,
AsyncPromise<RunContentDescriptor> runContentDescriptorPromise,
List<Supplier<DebuggerDriverConfiguration>> drivers) {
if (drivers.isEmpty()) {
runContentDescriptorPromise.setResult(null);
return;
}
val driverSupplier = drivers.remove(0);
val driver = driverSupplier.get();
AppExecutorUtil.getAppExecutorService().execute(() -> {
try {
if (!doExecuteAsyncWithDriver(state, toolchain, environment, runContentDescriptorPromise, driver)) {
ApplicationManager.getApplication().invokeLater(() -> doExecuteAsyncFetchNextDriver(state, toolchain, environment, runContentDescriptorPromise, drivers));
}
} catch (ExecutionException e) {
runContentDescriptorPromise.setError(e);
}
});
} }
@Override @Override
protected RunContentDescriptor doExecute(ProfileState state, AbstractZigToolchain toolchain, ExecutionEnvironment environment) protected void doExecuteAsync(ProfileState state,
throws ExecutionException { AbstractZigToolchain toolchain,
ExecutionEnvironment environment,
AsyncPromise<RunContentDescriptor> runContentDescriptorPromise) {
val project = environment.getProject(); val project = environment.getProject();
val drivers = ZigDebuggerDriverConfigurationProvider.findDebuggerConfigurations(project, false, false) val drivers = ZigDebuggerDriverConfigurationProvider.findDebuggerConfigurations(project, false, false)
.toList(); .collect(Collectors.toCollection(ArrayList::new));
for (val debuggerDriver: drivers) { ApplicationManager.getApplication()
if (debuggerDriver == null) .invokeLater(() -> doExecuteAsyncFetchNextDriver(state, toolchain, environment, runContentDescriptorPromise, drivers));
continue;
ZigDebugParametersBase<ProfileState> runParameters = getDebugParameters(state, environment, debuggerDriver, toolchain);
if (runParameters == null) {
continue;
}
return startSession(environment, new ZigLocalDebugProcessStarter(runParameters, state, environment));
}
return null;
} }
@Override @Override
@ -87,79 +143,21 @@ public abstract class ZigDebugRunnerBase<ProfileState extends ProfileStateBase<?
protected abstract @Nullable ZigDebugParametersBase<ProfileState> getDebugParameters(ProfileState state, ExecutionEnvironment environment, DebuggerDriverConfiguration debuggerDriver, AbstractZigToolchain toolchain) throws ExecutionException; protected abstract @Nullable ZigDebugParametersBase<ProfileState> getDebugParameters(ProfileState state, ExecutionEnvironment environment, DebuggerDriverConfiguration debuggerDriver, AbstractZigToolchain toolchain) throws ExecutionException;
@RequiredArgsConstructor @RequiredArgsConstructor
private class ZigLocalDebugProcessStarter extends XDebugProcessStarter { private static class SharedConsoleBuilder extends TextConsoleBuilder {
private final ZigDebugParametersBase<ProfileState> params; private final ConsoleView console;
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() {
@Override @Override
public @NotNull ConsoleView getConsole() { 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; return console;
} }
@Override @Override
public void addFilter(@NotNull Filter filter) { public void addFilter(@NotNull Filter filter) {
cb.addFilter(filter);
} }
@Override @Override
public void setViewer(boolean isViewer) { public void setViewer(boolean b) {
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;
} }
} }
} }

View file

@ -16,8 +16,8 @@
package com.falsepattern.zigbrains.debugger.runner.build; 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.PreLaunchAware;
import com.falsepattern.zigbrains.debugger.runner.base.PreLaunchProcessListener;
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugEmitBinaryInstaller; import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugEmitBinaryInstaller;
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugParametersBase; import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugParametersBase;
import com.falsepattern.zigbrains.project.execution.build.ProfileStateBuild; import com.falsepattern.zigbrains.project.execution.build.ProfileStateBuild;
@ -33,7 +33,6 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List;
import java.util.stream.Stream; import java.util.stream.Stream;
public class ZigDebugParametersBuild extends ZigDebugParametersBase<ProfileStateBuild> implements PreLaunchAware { 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); val commandLine = profileState.getCommandLine(toolchain, true);
Utils.executeCommandLineWithErrorChecks(commandLine); if (listener.executeCommandLineWithHook(commandLine))
return null;
val cfg = profileState.configuration(); val cfg = profileState.configuration();
val workingDir = cfg.getWorkingDirectory().getPath().orElse(null); val workingDir = cfg.getWorkingDirectory().getPath().orElse(null);
val exePath = profileState.configuration().getExePath().getPath(); val exePath = profileState.configuration().getExePath().getPath();
@ -96,8 +96,8 @@ public class ZigDebugParametersBuild extends ZigDebugParametersBase<ProfileState
} }
@Override @Override
public void preLaunch() throws Exception { public void preLaunch(PreLaunchProcessListener listener) throws ExecutionException {
this.executableFile = compileExe(); this.executableFile = compileExe(listener);
} }
@Override @Override

View file

@ -61,7 +61,6 @@ public abstract class ProfileStateBase<T extends ZigExecConfigBase<T>> extends C
cli.setExePath(zigExecutablePath.toString()); cli.setExePath(zigExecutablePath.toString());
workingDirectory.getPath().ifPresent(x -> cli.setWorkDirectory(x.toFile())); workingDirectory.getPath().ifPresent(x -> cli.setWorkDirectory(x.toFile()));
cli.setCharset(StandardCharsets.UTF_8); cli.setCharset(StandardCharsets.UTF_8);
cli.setRedirectErrorStream(true);
cli.addParameters(configuration.buildCommandLineArgs(debug)); cli.addParameters(configuration.buildCommandLineArgs(debug));
return cli; return cli;
} }

View file

@ -52,7 +52,7 @@ public class ZigExecConfigRun extends ZigExecConfigBase<ZigExecConfigRun> {
@Override @Override
public List<String> buildCommandLineArgs(boolean debug) { public List<String> buildCommandLineArgs(boolean debug) {
val result = new ArrayList<String>(); val result = new ArrayList<String>();
result.add("run"); result.add(debug ? "build-exe" : "run");
result.addAll(CLIUtil.colored(colored.value, debug)); result.addAll(CLIUtil.colored(colored.value, debug));
result.add(filePath.getPathOrThrow().toString()); result.add(filePath.getPathOrThrow().toString());
if (!debug || optimization.forced) { if (!debug || optimization.forced) {

View file

@ -22,15 +22,20 @@ import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
import com.intellij.execution.ExecutionException; import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.RunProfileState; import com.intellij.execution.configurations.RunProfileState;
import com.intellij.execution.configurations.RunnerSettings; import com.intellij.execution.configurations.RunnerSettings;
import com.intellij.execution.runners.AsyncProgramRunner;
import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.runners.GenericProgramRunner;
import com.intellij.execution.ui.RunContentDescriptor; import com.intellij.execution.ui.RunContentDescriptor;
import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.util.concurrency.AppExecutorUtil;
import lombok.val; import lombok.val;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; 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; protected final String executorId;
public ZigProgramRunnerBase(String executorId) { public ZigProgramRunnerBase(String executorId) {
@ -38,33 +43,36 @@ public abstract class ZigProgramRunnerBase<ProfileState extends ProfileStateBase
} }
@Override @Override
protected void execute(@NotNull ExecutionEnvironment environment, @NotNull RunProfileState state) { protected Promise<RunContentDescriptor> execute(@NotNull ExecutionEnvironment environment, @NotNull RunProfileState state$) {
super.execute(environment, state);
}
@Override
protected @Nullable RunContentDescriptor doExecute(@NotNull RunProfileState state$, @NotNull ExecutionEnvironment environment)
throws ExecutionException {
if (!(state$ instanceof ProfileStateBase<?> state$$)) { if (!(state$ instanceof ProfileStateBase<?> state$$)) {
return null; return Promises.resolvedPromise();
} }
val state = castProfileState(state$$); val state = castProfileState(state$$);
if (state == null) if (state == null)
return null; return Promises.resolvedPromise();
val toolchain = ZigProjectSettingsService.getInstance(environment.getProject()).getState().getToolchain(); val toolchain = ZigProjectSettingsService.getInstance(environment.getProject()).getState().getToolchain();
if (toolchain == null) { if (toolchain == null) {
return null; return Promises.resolvedPromise();
} }
FileDocumentManager.getInstance().saveAllDocuments(); 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 ProfileState castProfileState(ProfileStateBase<?> state);
protected abstract @Nullable RunContentDescriptor doExecute(ProfileState state, protected abstract void doExecuteAsync(ProfileState state,
AbstractZigToolchain toolchain, AbstractZigToolchain toolchain,
ExecutionEnvironment environment) throws ExecutionException; ExecutionEnvironment environment,
AsyncPromise<RunContentDescriptor> runContentDescriptorPromise) throws ExecutionException;
} }

View file

@ -25,9 +25,11 @@ import com.intellij.execution.executors.DefaultRunExecutor;
import com.intellij.execution.runners.DefaultProgramRunnerKt; import com.intellij.execution.runners.DefaultProgramRunnerKt;
import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.ui.RunContentDescriptor; import com.intellij.execution.ui.RunContentDescriptor;
import com.intellij.openapi.application.ApplicationManager;
import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.concurrency.AsyncPromise;
public class ZigRegularRunner extends ZigProgramRunnerBase<ProfileStateBase<?>> { public class ZigRegularRunner extends ZigProgramRunnerBase<ProfileStateBase<?>> {
public ZigRegularRunner() { public ZigRegularRunner() {
@ -50,8 +52,17 @@ public class ZigRegularRunner extends ZigProgramRunnerBase<ProfileStateBase<?>>
} }
@Override @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 { 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);
}
});
} }
} }

View file

@ -50,14 +50,14 @@ public class ZLSStreamConnectionProvider extends OSProcessStreamConnectionProvid
setCommandLine(commandLine); setCommandLine(commandLine);
} }
public static List<String> doGetCommand(Project project, boolean warn) { public static List<String> doGetCommand(Project project, boolean safe) {
var svc = ZLSProjectSettingsService.getInstance(project); var svc = ZLSProjectSettingsService.getInstance(project);
val state = svc.getState(); val state = svc.getState();
var zlsPath = state.zlsPath; var zlsPath = state.zlsPath;
if (StringUtil.isEmpty(zlsPath)) { if (StringUtil.isEmpty(zlsPath)) {
zlsPath = com.falsepattern.zigbrains.common.util.FileUtil.findExecutableOnPATH("zls").map(Path::toString).orElse(null); zlsPath = com.falsepattern.zigbrains.common.util.FileUtil.findExecutableOnPATH("zls").map(Path::toString).orElse(null);
if (zlsPath == null) { if (zlsPath == null) {
if (warn) { if (safe) {
Notifications.Bus.notify( Notifications.Bus.notify(
new Notification("ZigBrains.ZLS", "Could not detect ZLS binary! Please configure it!", new Notification("ZigBrains.ZLS", "Could not detect ZLS binary! Please configure it!",
NotificationType.ERROR)); NotificationType.ERROR));
@ -66,19 +66,19 @@ public class ZLSStreamConnectionProvider extends OSProcessStreamConnectionProvid
} }
state.setZlsPath(zlsPath); state.setZlsPath(zlsPath);
} }
if (!validatePath("ZLS Binary", zlsPath, false, warn)) { if (!validatePath("ZLS Binary", zlsPath, false, safe)) {
return null; return null;
} }
var configPath = state.zlsConfigPath; var configPath = state.zlsConfigPath;
boolean configOK = true; boolean configOK = true;
if (!configPath.isBlank() && !validatePath("ZLS Config", configPath, false, warn)) { if (!configPath.isBlank() && !validatePath("ZLS Config", configPath, false, safe)) {
if (warn) { if (safe) {
Notifications.Bus.notify( Notifications.Bus.notify(
new Notification("ZigBrains.ZLS", "Using default config path.", NotificationType.INFORMATION)); new Notification("ZigBrains.ZLS", "Using default config path.", NotificationType.INFORMATION));
} }
configPath = null; configPath = null;
} }
if (configPath == null || configPath.isBlank()) { if ((configPath == null || configPath.isBlank()) && safe) {
blk: blk:
try { try {
val tmpFile = FileUtil.createTempFile("zigbrains-zls-autoconf", ".json", true).toPath(); val tmpFile = FileUtil.createTempFile("zigbrains-zls-autoconf", ".json", true).toPath();
@ -95,7 +95,7 @@ public class ZLSStreamConnectionProvider extends OSProcessStreamConnectionProvid
} }
configPath = tmpFile.toAbsolutePath().toString(); configPath = tmpFile.toAbsolutePath().toString();
} catch (IOException e) { } catch (IOException e) {
if (warn) { if (safe) {
Notifications.Bus.notify( Notifications.Bus.notify(
new Notification("ZigBrains.ZLS", "Failed to create automatic zls config file", new Notification("ZigBrains.ZLS", "Failed to create automatic zls config file",
NotificationType.WARNING)); NotificationType.WARNING));
@ -140,7 +140,7 @@ public class ZLSStreamConnectionProvider extends OSProcessStreamConnectionProvid
return future; return future;
} }
private static boolean validatePath(String name, String pathTxt, boolean dir, boolean warn) { private static boolean validatePath(String name, String pathTxt, boolean dir, boolean safe) {
if (pathTxt == null || pathTxt.isBlank()) { if (pathTxt == null || pathTxt.isBlank()) {
return false; return false;
} }
@ -148,7 +148,7 @@ public class ZLSStreamConnectionProvider extends OSProcessStreamConnectionProvid
try { try {
path = Path.of(pathTxt); path = Path.of(pathTxt);
} catch (InvalidPathException e) { } catch (InvalidPathException e) {
if (warn) { if (safe) {
Notifications.Bus.notify(new Notification("ZigBrains.ZLS", "No " + name, Notifications.Bus.notify(new Notification("ZigBrains.ZLS", "No " + name,
"Invalid " + name + " at path \"" + pathTxt + "\"", "Invalid " + name + " at path \"" + pathTxt + "\"",
NotificationType.ERROR)); NotificationType.ERROR));
@ -156,7 +156,7 @@ public class ZLSStreamConnectionProvider extends OSProcessStreamConnectionProvid
return false; return false;
} }
if (!Files.exists(path)) { if (!Files.exists(path)) {
if (warn) { if (safe) {
Notifications.Bus.notify(new Notification("ZigBrains.ZLS", "No " + name, Notifications.Bus.notify(new Notification("ZigBrains.ZLS", "No " + name,
"The " + name + " at \"" + pathTxt + "\" doesn't exist!", "The " + name + " at \"" + pathTxt + "\" doesn't exist!",
NotificationType.ERROR)); NotificationType.ERROR));
@ -164,7 +164,7 @@ public class ZLSStreamConnectionProvider extends OSProcessStreamConnectionProvid
return false; return false;
} }
if (Files.isDirectory(path) != dir) { if (Files.isDirectory(path) != dir) {
if (warn) { if (safe) {
Notifications.Bus.notify(new Notification("ZigBrains.ZLS", "No " + name, Notifications.Bus.notify(new Notification("ZigBrains.ZLS", "No " + name,
"The " + name + " at \"" + pathTxt + "\" is a " + "The " + name + " at \"" + pathTxt + "\" is a " +
(Files.isDirectory(path) ? "directory" : "file") + (Files.isDirectory(path) ? "directory" : "file") +