backport: 14.1.0

This commit is contained in:
FalsePattern 2024-05-11 15:23:39 +02:00
parent a985dc18e9
commit 5328d6f498
Signed by: falsepattern
GPG key ID: E930CDEC50C50E23
19 changed files with 305 additions and 184 deletions

View file

@ -11,13 +11,26 @@ Changelog structure reference:
"Category" should be something that can be quickly recognized by readers ("Highlighting", "Code Completion", "Folding", etc.) "Category" should be something that can be quickly recognized by readers ("Highlighting", "Code Completion", "Folding", etc.)
"Type" ALWAYS follows the order in the list above "Type" ALWAYS follows the order in the list above
"Category" ALWAYS alphabetically sorted
--> -->
# ZigBrains # ZigBrains
## [Unreleased] ## [Unreleased]
## [14.1.0]
### Fixed
- Debugging
- Huge rework for starting the various debugging runs, and more robust compilation error visualization instead of a tiny
popup
- LSP
- No more notification popup about zig env not being detected when not in a zig projects.
- Project
- ZLS should now be detected more reliably when creating new projects
## [14.0.1] ## [14.0.1]
### Fixed ### Fixed

View file

@ -22,15 +22,17 @@ import lombok.val;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ApplicationUtil { public class ApplicationUtil {
private final static ExecutorService EXECUTOR_SERVICE; private final static ScheduledExecutorService EXECUTOR_SERVICE;
static { static {
// Single threaded executor is used to simulate a behavior of async sequencial execution. // Single threaded executor is used to simulate a behavior of async sequencial execution.
// All runnables are executed asyncly but they are executed in the order of their submission. // All runnables are executed asyncly but they are executed in the order of their submission.
EXECUTOR_SERVICE = Executors.newSingleThreadExecutor(); EXECUTOR_SERVICE = Executors.newSingleThreadScheduledExecutor();
Runtime.getRuntime().addShutdownHook(new Thread() { Runtime.getRuntime().addShutdownHook(new Thread() {
@Override @Override
public void run() { public void run() {
@ -48,6 +50,10 @@ public class ApplicationUtil {
EXECUTOR_SERVICE.submit(runnable); EXECUTOR_SERVICE.submit(runnable);
} }
public static void pool(Runnable runnable, long delay, TimeUnit timeUnit) {
EXECUTOR_SERVICE.schedule(runnable, delay, timeUnit);
}
static public <T> T computableReadAction(Computable<T> computable) { static public <T> T computableReadAction(Computable<T> computable) {
return ApplicationManager.getApplication().runReadAction(computable); return ApplicationManager.getApplication().runReadAction(computable);
} }

View file

@ -18,8 +18,6 @@ package com.falsepattern.zigbrains.debugger.runner.base;
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.falsepattern.zigbrains.project.util.CLIUtil;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.execution.configurations.GeneralCommandLine;
import com.jetbrains.cidr.execution.Installer; import com.jetbrains.cidr.execution.Installer;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@ -27,61 +25,27 @@ import lombok.val;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
@RequiredArgsConstructor @RequiredArgsConstructor
public abstract class ZigDebugEmitBinaryInstaller<ProfileState extends ProfileStateBase<?>> implements Installer { public class ZigDebugEmitBinaryInstaller<ProfileState extends ProfileStateBase<?>> implements Installer {
protected final String kind;
protected final ProfileState profileState; protected final ProfileState profileState;
protected final AbstractZigToolchain toolchain; protected final AbstractZigToolchain toolchain;
private File executableFile; private final File executableFile;
private final String[] exeArgs;
@Override @Override
public @NotNull GeneralCommandLine install() throws ExecutionException { public @NotNull GeneralCommandLine install() {
val commandLine = profileState.getCommandLine(toolchain, true);
final Path tmpDir;
try {
tmpDir = Files.createTempDirectory("zigbrains_debug").toAbsolutePath();
} catch (IOException e) {
throw new ExecutionException("Failed to create temporary directory for " + kind + " binary", e);
}
val exe = tmpDir.resolve("executable").toFile();
commandLine.addParameters("-femit-bin=" + exe.getAbsolutePath());
val outputOpt = CLIUtil.execute(commandLine, Integer.MAX_VALUE);
if (outputOpt.isEmpty()) {
throw new ExecutionException("Failed to execute \"zig " + commandLine.getParametersList().getParametersString() + "\"!");
}
val output = outputOpt.get();
if (output.getExitCode() != 0) {
throw new ExecutionException("Zig compilation failed with exit code " + output.getExitCode() + "\nError output:\n" + output.getStdout() + "\n" + output.getStderr());
}
//Find our binary
try (val stream = Files.list(tmpDir)){
executableFile = stream.filter(file -> !file.getFileName().toString().endsWith(".o"))
.map(Path::toFile)
.filter(File::canExecute)
.findFirst()
.orElseThrow(() -> new IOException("No executable file present in temporary directory \"" +
tmpDir + "\""));
} catch (Exception e) {
throw new ExecutionException("Failed to find compiled binary", e);
}
//Construct new command line //Construct new command line
val cfg = profileState.configuration(); val cfg = profileState.configuration();
val cli = new GeneralCommandLine().withExePath(executableFile.getAbsolutePath()); val cli = new GeneralCommandLine().withExePath(executableFile.getAbsolutePath());
cfg.getWorkingDirectory().getPath().ifPresent(x -> cli.setWorkDirectory(x.toFile())); cfg.getWorkingDirectory().getPath().ifPresent(x -> cli.setWorkDirectory(x.toFile()));
cli.addParameters(getExeArgs()); cli.addParameters(exeArgs);
cli.withCharset(StandardCharsets.UTF_8); cli.withCharset(StandardCharsets.UTF_8);
cli.withRedirectErrorStream(true); cli.withRedirectErrorStream(true);
return cli; return cli;
} }
public abstract String[] getExeArgs();
@Override @Override
public @NotNull File getExecutableFile() { public @NotNull File getExecutableFile() {
return executableFile; return executableFile;

View file

@ -0,0 +1,64 @@
/*
* 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.runner.base;
import com.falsepattern.zigbrains.project.execution.base.ProfileStateBase;
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
import com.falsepattern.zigbrains.project.util.CLIUtil;
import com.intellij.execution.ExecutionException;
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration;
import lombok.val;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public abstract class ZigDebugParametersEmitBinaryBase<ProfileState extends ProfileStateBase<?>> extends ZigDebugParametersBase<ProfileState> {
protected final File executableFile;
public ZigDebugParametersEmitBinaryBase(DebuggerDriverConfiguration driverConfiguration, AbstractZigToolchain toolchain, ProfileState profileState, String kind) throws ExecutionException {
super(driverConfiguration, toolchain, profileState);
val commandLine = profileState.getCommandLine(toolchain, true);
final Path tmpDir;
try {
tmpDir = Files.createTempDirectory("zigbrains_debug").toAbsolutePath();
} catch (IOException e) {
throw new ExecutionException("Failed to create temporary directory for " + kind + " binary", e);
}
val exe = tmpDir.resolve("executable").toFile();
commandLine.addParameters("-femit-bin=" + exe.getAbsolutePath());
val outputOpt = CLIUtil.execute(commandLine, Integer.MAX_VALUE);
if (outputOpt.isEmpty()) {
throw new ExecutionException("Failed to execute \"zig " + commandLine.getParametersList().getParametersString() + "\"!");
}
val output = outputOpt.get();
if (output.getExitCode() != 0) {
throw new ExecutionException("Zig compilation failed with exit code " + output.getExitCode() + "\nError output:\n" + output.getStdout() + "\n" + output.getStderr());
}
//Find our binary
try (val stream = Files.list(tmpDir)){
executableFile = stream.filter(file -> !file.getFileName().toString().endsWith(".o"))
.map(Path::toFile)
.filter(File::canExecute)
.findFirst()
.orElseThrow(() -> new IOException("No executable file present in temporary directory \"" +
tmpDir + "\""));
} catch (Exception e) {
throw new ExecutionException("Failed to find compiled binary! " + e.getMessage(), e);
}
}
}

View file

@ -22,28 +22,47 @@ import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
import com.falsepattern.zigbrains.debugger.Utils; import com.falsepattern.zigbrains.debugger.Utils;
import com.falsepattern.zigbrains.debugger.ZigLocalDebugProcess; import com.falsepattern.zigbrains.debugger.ZigLocalDebugProcess;
import com.intellij.execution.ExecutionException; import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.configurations.RunProfile; import com.intellij.execution.configurations.RunProfile;
import com.intellij.execution.executors.DefaultDebugExecutor; import com.intellij.execution.executors.DefaultDebugExecutor;
import com.intellij.execution.filters.Filter;
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.ui.ConsoleView;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.execution.ui.RunContentDescriptor; import com.intellij.execution.ui.RunContentDescriptor;
import com.intellij.notification.Notification; import com.intellij.notification.Notification;
import com.intellij.notification.NotificationType; import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications; import com.intellij.notification.Notifications;
import com.intellij.openapi.application.ApplicationManager;
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.Installer;
import com.jetbrains.cidr.execution.TrivialRunParameters;
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration; import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration;
import lombok.RequiredArgsConstructor;
import lombok.val; import lombok.val;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.io.File;
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)
throws ExecutionException {
return XDebuggerManager.getInstance(environment.getProject())
.startSession(environment, starter)
.getRunContentDescriptor();
}
@Override @Override
protected RunContentDescriptor doExecute(ProfileState state, AbstractZigToolchain toolchain, ExecutionEnvironment environment) protected RunContentDescriptor doExecute(ProfileState state, AbstractZigToolchain toolchain, ExecutionEnvironment environment)
throws ExecutionException { throws ExecutionException {
@ -53,26 +72,97 @@ public abstract class ZigDebugRunnerBase<ProfileState extends ProfileStateBase<?
Notifications.Bus.notify(new Notification("ZigBrains.Debugger.Error", "Couldn't find a working GDB or LLDB debugger! Please check your Toolchains! (Settings | Build, Execution, Deployment | Toolchains)", NotificationType.ERROR)); Notifications.Bus.notify(new Notification("ZigBrains.Debugger.Error", "Couldn't find a working GDB or LLDB debugger! Please check your Toolchains! (Settings | Build, Execution, Deployment | Toolchains)", NotificationType.ERROR));
return null; return null;
} }
val runParameters = getDebugParameters(state, environment, debuggerDriver, toolchain); Either<ZigDebugParametersBase<ProfileState>, ExecutionException> runParameters;
try {
runParameters = ApplicationManager.getApplication().executeOnPooledThread(() -> getDebugParametersSafe(state, environment, debuggerDriver, toolchain)).get();
} catch (InterruptedException | java.util.concurrent.ExecutionException e) {
e.printStackTrace();
Notifications.Bus.notify(new Notification("ZigBrains.Debugger.Error", e.getMessage(), NotificationType.ERROR));
return null;
}
if (runParameters == null) { if (runParameters == null) {
//Assume that getDebugParameters reports the bug in a notification already //Assume that getDebugParameters reports the bug in a notification already
return null; return null;
} }
val manager = XDebuggerManager.getInstance(project);
return manager.startSession(environment, if (runParameters.isRight()) {
new XDebugProcessStarter() { return startSession(environment, new ErrorProcessStarter(state, runParameters.getRight(), debuggerDriver));
@Override } else if (runParameters.isLeft()) {
public @NotNull XDebugProcess start(@NotNull XDebugSession session) throws ExecutionException { return startSession(environment, new ZigLocalDebugProcessStarter(runParameters.getLeft(), state, environment));
val process = new ZigLocalDebugProcess(runParameters, session, state.getConsoleBuilder()); } else {
ProcessTerminatedListener.attach(process.getProcessHandler(), environment.getProject()); return null;
process.start(); }
return process; }
}
}).getRunContentDescriptor(); private Either<ZigDebugParametersBase<ProfileState>, ExecutionException> getDebugParametersSafe(ProfileState state, ExecutionEnvironment environment, DebuggerDriverConfiguration debuggerDriver, AbstractZigToolchain toolchain) {
try {
return Either.forLeft(getDebugParameters(state, environment, debuggerDriver, toolchain));
} catch (ExecutionException e) {
return Either.forRight(e);
}
} }
@Override @Override
public abstract boolean canRun(@NotNull String executorId, @NotNull RunProfile profile); public abstract boolean canRun(@NotNull String executorId, @NotNull RunProfile profile);
protected abstract @Nullable ZigDebugParametersBase<ProfileState> getDebugParameters(ProfileState state, ExecutionEnvironment environment, DebuggerDriverConfiguration debuggerDriver, AbstractZigToolchain toolchain); 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;
@Override
public @NotNull XDebugProcess start(@NotNull XDebugSession session) throws ExecutionException {
val process = new ZigLocalDebugProcess(params, session, state.getConsoleBuilder());
ProcessTerminatedListener.attach(process.getProcessHandler(), environment.getProject());
process.start();
return process;
}
}
@RequiredArgsConstructor
private class ErrorProcessStarter extends XDebugProcessStarter {
private final ProfileState state;
private final ExecutionException exception;
private final DebuggerDriverConfiguration debuggerDriver;
@Override
public @NotNull XDebugProcess start(@NotNull XDebugSession session) throws ExecutionException {
val cb = state.getConsoleBuilder();
val wrappedBuilder = new TextConsoleBuilder() {
@Override
public @NotNull ConsoleView getConsole() {
val console = cb.getConsole();
console.print(exception.getMessage(), ConsoleViewContentType.ERROR_OUTPUT);
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(new TrivialRunParameters(debuggerDriver, new Installer() {
@Override
public @NotNull GeneralCommandLine install() throws ExecutionException {
throw new ExecutionException("Failed to start debugging");
}
@Override
public @NotNull File getExecutableFile() {
return null;
}
}), session, wrappedBuilder);
process.start();
return process;
}
}
} }

View file

@ -16,6 +16,7 @@
package com.falsepattern.zigbrains.debugger.runner.binary; package com.falsepattern.zigbrains.debugger.runner.binary;
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.debugger.execution.binary.ProfileStateBinary; import com.falsepattern.zigbrains.debugger.execution.binary.ProfileStateBinary;
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain; import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
@ -29,25 +30,14 @@ import org.jetbrains.annotations.NotNull;
import java.io.File; import java.io.File;
public class ZigDebugParametersBinary extends ZigDebugParametersBase<ProfileStateBinary> { public class ZigDebugParametersBinary extends ZigDebugParametersBase<ProfileStateBinary> {
public ZigDebugParametersBinary(DebuggerDriverConfiguration driverConfiguration, AbstractZigToolchain toolchain, ProfileStateBinary profileStateBinary) { private final File executableFile;
public ZigDebugParametersBinary(DebuggerDriverConfiguration driverConfiguration, AbstractZigToolchain toolchain, ProfileStateBinary profileStateBinary) throws ExecutionException {
super(driverConfiguration, toolchain, profileStateBinary); super(driverConfiguration, toolchain, profileStateBinary);
executableFile = profileState.configuration().getExePath().getPathOrThrow().toFile();
} }
@Override @Override
public @NotNull Installer getInstaller() { public @NotNull Installer getInstaller() {
return new Installer() { return new ZigDebugEmitBinaryInstaller<>(profileState, toolchain, executableFile, profileState.configuration().getArgs().args);
private File executableFile;
@Override
public @NotNull GeneralCommandLine install() throws ExecutionException {
val cli = profileState.getCommandLine(toolchain, true);
executableFile = profileState.configuration().getExePath().getPathOrThrow().toFile();
return cli;
}
@Override
public @NotNull File getExecutableFile() {
return executableFile;
}
};
} }
} }

View file

@ -23,6 +23,7 @@ import com.falsepattern.zigbrains.debugger.execution.binary.ProfileStateBinary;
import com.falsepattern.zigbrains.debugger.execution.binary.ZigExecConfigBinary; import com.falsepattern.zigbrains.debugger.execution.binary.ZigExecConfigBinary;
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain; import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain; import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.RunProfile; import com.intellij.execution.configurations.RunProfile;
import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.notification.Notification; import com.intellij.notification.Notification;
@ -46,12 +47,8 @@ public class ZigDebugRunnerBinary extends ZigDebugRunnerBase<ProfileStateBinary>
} }
@Override @Override
protected @Nullable ZigDebugParametersBase<ProfileStateBinary> getDebugParameters(ProfileStateBinary profileStateBinary, ExecutionEnvironment environment, DebuggerDriverConfiguration debuggerDriver, AbstractZigToolchain toolchain$) { protected @Nullable ZigDebugParametersBase<ProfileStateBinary> getDebugParameters(ProfileStateBinary profileStateBinary, ExecutionEnvironment environment, DebuggerDriverConfiguration debuggerDriver, AbstractZigToolchain toolchain) throws ExecutionException {
if (!(toolchain$ instanceof LocalZigToolchain toolchain)) { return new ZigDebugParametersBinary(debuggerDriver, LocalZigToolchain.ensureLocal(toolchain), profileStateBinary);
Notifications.Bus.notify(new Notification("ZigBrains.Debugger.Error", "The debugger only supports local zig toolchains!", NotificationType.ERROR));
return null;
}
return new ZigDebugParametersBinary(debuggerDriver, toolchain, profileStateBinary);
} }
@Override @Override

View file

@ -16,6 +16,7 @@
package com.falsepattern.zigbrains.debugger.runner.build; package com.falsepattern.zigbrains.debugger.runner.build;
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;
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain; import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
@ -37,74 +38,57 @@ import java.util.Arrays;
public class ZigDebugParametersBuild extends ZigDebugParametersBase<ProfileStateBuild> { public class ZigDebugParametersBuild extends ZigDebugParametersBase<ProfileStateBuild> {
private static final String BoilerplateNotice = "\nPlease edit this intellij build configuration and specify the path of the executable created by \"zig build\" directly!"; private static final String BoilerplateNotice = "\nPlease edit this intellij build configuration and specify the path of the executable created by \"zig build\" directly!";
public ZigDebugParametersBuild(DebuggerDriverConfiguration driverConfiguration, AbstractZigToolchain toolchain, ProfileStateBuild profileStateBuild) { private final File executableFile;
public ZigDebugParametersBuild(DebuggerDriverConfiguration driverConfiguration, AbstractZigToolchain toolchain, ProfileStateBuild profileStateBuild) throws ExecutionException {
super(driverConfiguration, toolchain, profileStateBuild); super(driverConfiguration, toolchain, profileStateBuild);
val commandLine = profileState.getCommandLine(toolchain, true);
val outputOpt = CLIUtil.execute(commandLine, Integer.MAX_VALUE);
if (outputOpt.isEmpty()) {
throw new ExecutionException("Failed to execute \"zig " + commandLine.getParametersList().getParametersString() + "\"!");
}
val output = outputOpt.get();
if (output.getExitCode() != 0) {
throw new ExecutionException("Zig compilation failed with exit code " + output.getExitCode() + "\nError output:\n" + output.getStdout() + "\n" + output.getStderr());
}
val cfg = profileState.configuration();
val workingDir = cfg.getWorkingDirectory().getPath().orElse(null);
val exePath = profileState.configuration().getExePath().getPath();
Path exe;
if (exePath.isEmpty()) {
//Attempt autodetect, should work for trivial cases, and make basic users happy, while advanced
// users can use manual executable paths.
if (workingDir == null) {
throw new ExecutionException("Cannot find working directory to run debugged executable!" + BoilerplateNotice);
}
val expectedOutputDir = workingDir.resolve(Path.of("zig-out", "bin"));
if (!Files.exists(expectedOutputDir)) {
throw new ExecutionException("Could not auto-detect default executable output directory \"zig-out/bin\"!" + BoilerplateNotice);
}
try (val filesInOutput = Files.list(expectedOutputDir)) {
val executables = filesInOutput.filter(Files::isRegularFile).filter(Files::isExecutable).toList();
if (executables.size() > 1) {
throw new ExecutionException("Multiple executables found!" + BoilerplateNotice);
}
exe = executables.get(0);
} catch (IOException e) {
throw new ExecutionException("Could not scan output directory \"" + expectedOutputDir + "\"!" + BoilerplateNotice);
}
} else {
exe = exePath.get();
}
if (!Files.exists(exe)) {
throw new ExecutionException("File " + exe + " does not exist!");
} else if (!Files.isExecutable(exe)) {
throw new ExecutionException("File " + exe + " is not executable!");
}
executableFile = exe.toFile();
} }
@Override @Override
public @NotNull Installer getInstaller() { public @NotNull Installer getInstaller() {
return new Installer() { return new ZigDebugEmitBinaryInstaller<>(profileState, toolchain, executableFile, profileState.configuration().getExeArgs().args);
private File executableFile;
@Override
public @NotNull GeneralCommandLine install() throws ExecutionException {
val commandLine = profileState.getCommandLine(toolchain, true);
val outputOpt = CLIUtil.execute(commandLine, Integer.MAX_VALUE);
if (outputOpt.isEmpty()) {
throw new ExecutionException("Failed to execute \"zig " + commandLine.getParametersList().getParametersString() + "\"!");
}
val output = outputOpt.get();
if (output.getExitCode() != 0) {
throw new ExecutionException("Zig compilation failed with exit code " + output.getExitCode() + "\nError output:\n" + output.getStdout() + "\n" + output.getStderr());
}
val cfg = profileState.configuration();
val workingDir = cfg.getWorkingDirectory().getPath().orElse(null);
val exePath = profileState.configuration().getExePath().getPath();
Path exe;
if (exePath.isEmpty()) {
//Attempt autodetect, should work for trivial cases, and make basic users happy, while advanced
// users can use manual executable paths.
if (workingDir == null) {
throw new ExecutionException("Cannot find working directory to run debugged executable!" + BoilerplateNotice);
}
val expectedOutputDir = workingDir.resolve(Path.of("zig-out", "bin"));
if (!Files.exists(expectedOutputDir)) {
throw new ExecutionException("Could not auto-detect default executable output directory \"zig-out/bin\"!" + BoilerplateNotice);
}
try (val filesInOutput = Files.list(expectedOutputDir)) {
val executables = filesInOutput.filter(Files::isRegularFile).filter(Files::isExecutable).toList();
if (executables.size() > 1) {
throw new ExecutionException("Multiple executables found!" + BoilerplateNotice);
}
exe = executables.get(0);
} catch (IOException e) {
throw new ExecutionException("Could not scan output directory \"" + expectedOutputDir + "\"!" + BoilerplateNotice);
}
} else {
exe = exePath.get();
}
if (!Files.exists(exe)) {
throw new ExecutionException("File " + exe + " does not exist!");
} else if (!Files.isExecutable(exe)) {
throw new ExecutionException("File " + exe + " is not executable!");
}
executableFile = exe.toFile();
//Construct new command line
val cli = new GeneralCommandLine().withExePath(executableFile.getAbsolutePath());
cfg.getWorkingDirectory().getPath().ifPresent(x -> cli.setWorkDirectory(x.toFile()));
cli.addParameters(cfg.getExeArgs().args);
cli.withCharset(StandardCharsets.UTF_8);
cli.withRedirectErrorStream(true);
return cli;
}
@Override
public @NotNull File getExecutableFile() {
return executableFile;
}
};
} }
} }

View file

@ -23,12 +23,14 @@ import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain; import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain;
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugParametersBase; import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugParametersBase;
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugRunnerBase; import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugRunnerBase;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.RunProfile; import com.intellij.execution.configurations.RunProfile;
import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.notification.Notification; import com.intellij.notification.Notification;
import com.intellij.notification.NotificationType; import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications; import com.intellij.notification.Notifications;
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration; import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration;
import lombok.val;
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;
@ -46,12 +48,9 @@ public class ZigDebugRunnerBuild extends ZigDebugRunnerBase<ProfileStateBuild> {
} }
@Override @Override
protected @Nullable ZigDebugParametersBase<ProfileStateBuild> getDebugParameters(ProfileStateBuild profileStateBuild, ExecutionEnvironment environment, DebuggerDriverConfiguration debuggerDriver, AbstractZigToolchain toolchain$) { protected @Nullable ZigDebugParametersBase<ProfileStateBuild> getDebugParameters(ProfileStateBuild profileStateBuild, ExecutionEnvironment environment, DebuggerDriverConfiguration debuggerDriver, AbstractZigToolchain toolchain) throws
if (!(toolchain$ instanceof LocalZigToolchain toolchain)) { ExecutionException {
Notifications.Bus.notify(new Notification("ZigBrains.Debugger.Error", "The debugger only supports local zig toolchains!", NotificationType.ERROR)); return new ZigDebugParametersBuild(debuggerDriver, LocalZigToolchain.ensureLocal(toolchain), profileStateBuild);
return null;
}
return new ZigDebugParametersBuild(debuggerDriver, toolchain, profileStateBuild);
} }
@Override @Override

View file

@ -18,24 +18,22 @@ package com.falsepattern.zigbrains.debugger.runner.run;
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.debugger.runner.base.ZigDebugParametersEmitBinaryBase;
import com.falsepattern.zigbrains.project.execution.run.ProfileStateRun; import com.falsepattern.zigbrains.project.execution.run.ProfileStateRun;
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain; import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
import com.intellij.execution.ExecutionException;
import com.jetbrains.cidr.execution.Installer; import com.jetbrains.cidr.execution.Installer;
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration; import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class ZigDebugParametersRun extends ZigDebugParametersBase<ProfileStateRun> { public class ZigDebugParametersRun extends ZigDebugParametersEmitBinaryBase<ProfileStateRun> {
public ZigDebugParametersRun(DebuggerDriverConfiguration driverConfiguration, AbstractZigToolchain toolchain, ProfileStateRun profileState) { public ZigDebugParametersRun(DebuggerDriverConfiguration driverConfiguration, AbstractZigToolchain toolchain, ProfileStateRun profileState)
super(driverConfiguration, toolchain, profileState); throws ExecutionException {
super(driverConfiguration, toolchain, profileState, "run");
} }
@Override @Override
public @NotNull Installer getInstaller() { public @NotNull Installer getInstaller() {
return new ZigDebugEmitBinaryInstaller<>("run", profileState, toolchain) { return new ZigDebugEmitBinaryInstaller<>(profileState, toolchain, executableFile, profileState.configuration().getExeArgs().args);
@Override
public String[] getExeArgs() {
return profileState.configuration().getExeArgs().args;
}
};
} }
} }

View file

@ -16,12 +16,14 @@
package com.falsepattern.zigbrains.debugger.runner.run; package com.falsepattern.zigbrains.debugger.runner.run;
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugParametersBase;
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugRunnerBase; import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugRunnerBase;
import com.falsepattern.zigbrains.project.execution.base.ProfileStateBase; import com.falsepattern.zigbrains.project.execution.base.ProfileStateBase;
import com.falsepattern.zigbrains.project.execution.run.ProfileStateRun; import com.falsepattern.zigbrains.project.execution.run.ProfileStateRun;
import com.falsepattern.zigbrains.project.execution.run.ZigExecConfigRun; import com.falsepattern.zigbrains.project.execution.run.ZigExecConfigRun;
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain; import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain; import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.RunProfile; import com.intellij.execution.configurations.RunProfile;
import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.notification.Notification; import com.intellij.notification.Notification;
@ -45,12 +47,9 @@ public class ZigDebugRunnerRun extends ZigDebugRunnerBase<ProfileStateRun> {
} }
@Override @Override
protected ZigDebugParametersRun getDebugParameters(ProfileStateRun profileStateRun, ExecutionEnvironment environment, DebuggerDriverConfiguration debuggerDriver, AbstractZigToolchain toolchain$) { protected ZigDebugParametersRun getDebugParameters(ProfileStateRun profileStateRun, ExecutionEnvironment environment, DebuggerDriverConfiguration debuggerDriver, AbstractZigToolchain toolchain) throws
if (!(toolchain$ instanceof LocalZigToolchain toolchain)) { ExecutionException {
Notifications.Bus.notify(new Notification("ZigBrains.Debugger.Error", "The debugger only supports local zig toolchains!", NotificationType.ERROR)); return new ZigDebugParametersRun(debuggerDriver, LocalZigToolchain.ensureLocal(toolchain), profileStateRun);
return null;
}
return new ZigDebugParametersRun(debuggerDriver, toolchain, profileStateRun);
} }
@Override @Override

View file

@ -16,26 +16,24 @@
package com.falsepattern.zigbrains.debugger.runner.test; package com.falsepattern.zigbrains.debugger.runner.test;
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugParametersEmitBinaryBase;
import com.falsepattern.zigbrains.project.execution.test.ProfileStateTest; import com.falsepattern.zigbrains.project.execution.test.ProfileStateTest;
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain; import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
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.intellij.execution.ExecutionException;
import com.jetbrains.cidr.execution.Installer; import com.jetbrains.cidr.execution.Installer;
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration; import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class ZigDebugParametersTest extends ZigDebugParametersBase<ProfileStateTest> { public class ZigDebugParametersTest extends ZigDebugParametersEmitBinaryBase<ProfileStateTest> {
public ZigDebugParametersTest(DebuggerDriverConfiguration driverConfiguration, AbstractZigToolchain toolchain, ProfileStateTest profileState) { public ZigDebugParametersTest(DebuggerDriverConfiguration driverConfiguration, AbstractZigToolchain toolchain, ProfileStateTest profileState)
super(driverConfiguration, toolchain, profileState); throws ExecutionException {
super(driverConfiguration, toolchain, profileState, "test");
} }
@Override @Override
public @NotNull Installer getInstaller() { public @NotNull Installer getInstaller() {
return new ZigDebugEmitBinaryInstaller<>("test", profileState, toolchain) { return new ZigDebugEmitBinaryInstaller<>(profileState, toolchain, executableFile, new String[0]);
@Override
public String[] getExeArgs() {
return new String[0];
}
};
} }
} }

View file

@ -23,6 +23,7 @@ import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain; import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain;
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugParametersBase; import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugParametersBase;
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugRunnerBase; import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugRunnerBase;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.RunProfile; import com.intellij.execution.configurations.RunProfile;
import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.notification.Notification; import com.intellij.notification.Notification;
@ -46,12 +47,9 @@ public class ZigDebugRunnerTest extends ZigDebugRunnerBase<ProfileStateTest> {
} }
@Override @Override
protected @Nullable ZigDebugParametersBase<ProfileStateTest> getDebugParameters(ProfileStateTest profileStateTest, ExecutionEnvironment environment, DebuggerDriverConfiguration debuggerDriver, AbstractZigToolchain toolchain$) { protected @Nullable ZigDebugParametersBase<ProfileStateTest> getDebugParameters(ProfileStateTest profileStateTest, ExecutionEnvironment environment, DebuggerDriverConfiguration debuggerDriver, AbstractZigToolchain toolchain) throws
if (!(toolchain$ instanceof LocalZigToolchain toolchain)) { ExecutionException {
Notifications.Bus.notify(new Notification("ZigBrains.Debugger.Error", "The debugger only supports local zig toolchains!", NotificationType.ERROR)); return new ZigDebugParametersTest(debuggerDriver, LocalZigToolchain.ensureLocal(toolchain), profileStateTest);
return null;
}
return new ZigDebugParametersTest(debuggerDriver, toolchain, profileStateTest);
} }
@Override @Override

View file

@ -64,7 +64,7 @@ public class ZigExecConfigBuild extends ZigExecConfigBase<ZigExecConfigBuild> {
steps = truncatedSteps; steps = truncatedSteps;
} }
result.addAll(steps); result.addAll(steps);
result.addAll(CLIUtil.colored(colored.value)); result.addAll(CLIUtil.colored(colored.value, debug));
result.addAll(List.of(extraArgs.args)); result.addAll(List.of(extraArgs.args));
return result; return result;
} }

View file

@ -46,7 +46,7 @@ public class ZigExecConfigRun extends ZigExecConfigBase<ZigExecConfigRun> {
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("run");
result.addAll(CLIUtil.colored(colored.value)); 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) {
result.addAll(List.of("-O", optimization.level.name())); result.addAll(List.of("-O", optimization.level.name()));

View file

@ -46,7 +46,7 @@ public class ZigExecConfigTest extends ZigExecConfigBase<ZigExecConfigTest> {
public List<String> buildCommandLineArgs(boolean debug) { public List<String> buildCommandLineArgs(boolean debug) {
val result = new ArrayList<String>(); val result = new ArrayList<String>();
result.add("test"); result.add("test");
result.addAll(CLIUtil.colored(colored.value)); 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) {
result.addAll(List.of("-O", optimization.level.name())); result.addAll(List.of("-O", optimization.level.name()));

View file

@ -17,6 +17,7 @@
package com.falsepattern.zigbrains.project.toolchain; package com.falsepattern.zigbrains.project.toolchain;
import com.falsepattern.zigbrains.common.util.PathUtil; import com.falsepattern.zigbrains.common.util.PathUtil;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.execution.configurations.GeneralCommandLine;
import java.nio.file.Path; import java.nio.file.Path;
@ -40,4 +41,11 @@ public class LocalZigToolchain extends AbstractZigToolchain{
public Path pathToExecutable(String toolName) { public Path pathToExecutable(String toolName) {
return PathUtil.pathToExecutable(getLocation(), toolName); return PathUtil.pathToExecutable(getLocation(), toolName);
} }
public static LocalZigToolchain ensureLocal(AbstractZigToolchain toolchain) throws ExecutionException {
if (!(toolchain instanceof LocalZigToolchain $toolchain)) {
throw new ExecutionException("The debugger only supports local zig toolchains!");
}
return $toolchain;
}
} }

View file

@ -27,6 +27,7 @@ import lombok.val;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.StringTokenizer; import java.util.StringTokenizer;
@ -121,7 +122,13 @@ public class CLIUtil {
return result.toArray(new String[0]); return result.toArray(new String[0]);
} }
public static List<String> colored(boolean colored) { public static List<String> colored(boolean colored, boolean debug) {
return List.of("--color", colored ? "on" : "off"); // TODO remove this check once JetBrains implements colored terminal in the debugger
// https://youtrack.jetbrains.com/issue/CPP-11622/ANSI-color-codes-not-honored-in-Debug-Run-Configuration-output-window
if (debug) {
return Collections.emptyList();
} else {
return List.of("--color", colored ? "on" : "off");
}
} }
} }

View file

@ -16,6 +16,7 @@
package com.falsepattern.zigbrains.zig.lsp; package com.falsepattern.zigbrains.zig.lsp;
import com.falsepattern.zigbrains.common.util.ApplicationUtil;
import com.falsepattern.zigbrains.common.util.StringUtil; import com.falsepattern.zigbrains.common.util.StringUtil;
import com.falsepattern.zigbrains.lsp.IntellijLanguageClient; import com.falsepattern.zigbrains.lsp.IntellijLanguageClient;
import com.falsepattern.zigbrains.lsp.utils.FileUtils; import com.falsepattern.zigbrains.lsp.utils.FileUtils;
@ -40,6 +41,7 @@ import java.nio.file.Files;
import java.nio.file.InvalidPathException; import java.nio.file.InvalidPathException;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
public class ZLSStartupActivity implements ProjectActivity { public class ZLSStartupActivity implements ProjectActivity {
@ -76,7 +78,8 @@ public class ZLSStartupActivity implements ProjectActivity {
val tmpFile = Files.createTempFile("zigbrains-zls-autoconf", ".json"); val tmpFile = Files.createTempFile("zigbrains-zls-autoconf", ".json");
val config = ZLSConfigProvider.findEnvironment(project); val config = ZLSConfigProvider.findEnvironment(project);
if (StringUtil.isEmpty(config.zig_exe_path()) && StringUtil.isEmpty(config.zig_lib_path())) { if (StringUtil.isEmpty(config.zig_exe_path()) && StringUtil.isEmpty(config.zig_lib_path())) {
Notifications.Bus.notify(new Notification("ZigBrains.ZLS", "(ZLS) Failed to detect zig path from project toolchain", NotificationType.WARNING)); // TODO this generates unnecessary noise in non-zig projects, find an alternative.
// Notifications.Bus.notify(new Notification("ZigBrains.ZLS", "(ZLS) Failed to detect zig path from project toolchain", NotificationType.WARNING));
configOK = false; configOK = false;
break blk; break blk;
} }
@ -172,6 +175,9 @@ public class ZLSStartupActivity implements ProjectActivity {
if (zlsPath == null) { if (zlsPath == null) {
//Project creation //Project creation
ApplicationUtil.pool(() -> {
initZLS(project);
}, 5, TimeUnit.SECONDS);
return null; return null;
} }