backport: 14.1.0
This commit is contained in:
parent
36a0c9a8eb
commit
6b21a75113
19 changed files with 305 additions and 184 deletions
15
CHANGELOG.md
15
CHANGELOG.md
|
@ -11,13 +11,26 @@ Changelog structure reference:
|
|||
"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
|
||||
"Category" ALWAYS alphabetically sorted
|
||||
-->
|
||||
|
||||
# ZigBrains
|
||||
|
||||
## [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]
|
||||
|
||||
### Fixed
|
||||
|
|
|
@ -22,15 +22,17 @@ import lombok.val;
|
|||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ApplicationUtil {
|
||||
|
||||
private final static ExecutorService EXECUTOR_SERVICE;
|
||||
private final static ScheduledExecutorService EXECUTOR_SERVICE;
|
||||
|
||||
static {
|
||||
// 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.
|
||||
EXECUTOR_SERVICE = Executors.newSingleThreadExecutor();
|
||||
EXECUTOR_SERVICE = Executors.newSingleThreadScheduledExecutor();
|
||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
@ -48,6 +50,10 @@ public class ApplicationUtil {
|
|||
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) {
|
||||
return ApplicationManager.getApplication().runReadAction(computable);
|
||||
}
|
||||
|
|
|
@ -18,8 +18,6 @@ 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.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.jetbrains.cidr.execution.Installer;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
@ -27,61 +25,27 @@ import lombok.val;
|
|||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public abstract class ZigDebugEmitBinaryInstaller<ProfileState extends ProfileStateBase<?>> implements Installer {
|
||||
protected final String kind;
|
||||
public class ZigDebugEmitBinaryInstaller<ProfileState extends ProfileStateBase<?>> implements Installer {
|
||||
protected final ProfileState profileState;
|
||||
protected final AbstractZigToolchain toolchain;
|
||||
private File executableFile;
|
||||
private final File executableFile;
|
||||
private final String[] exeArgs;
|
||||
|
||||
@Override
|
||||
public @NotNull GeneralCommandLine install() throws ExecutionException {
|
||||
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);
|
||||
}
|
||||
|
||||
public @NotNull GeneralCommandLine install() {
|
||||
//Construct new command line
|
||||
val cfg = profileState.configuration();
|
||||
val cli = new GeneralCommandLine().withExePath(executableFile.getAbsolutePath());
|
||||
cfg.getWorkingDirectory().getPath().ifPresent(x -> cli.setWorkDirectory(x.toFile()));
|
||||
cli.addParameters(getExeArgs());
|
||||
cli.addParameters(exeArgs);
|
||||
cli.withCharset(StandardCharsets.UTF_8);
|
||||
cli.withRedirectErrorStream(true);
|
||||
return cli;
|
||||
}
|
||||
|
||||
public abstract String[] getExeArgs();
|
||||
|
||||
@Override
|
||||
public @NotNull File getExecutableFile() {
|
||||
return executableFile;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,28 +22,47 @@ import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
|||
import com.falsepattern.zigbrains.debugger.Utils;
|
||||
import com.falsepattern.zigbrains.debugger.ZigLocalDebugProcess;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.intellij.execution.configurations.RunProfile;
|
||||
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.runners.ExecutionEnvironment;
|
||||
import com.intellij.execution.ui.ConsoleView;
|
||||
import com.intellij.execution.ui.ConsoleViewContentType;
|
||||
import com.intellij.execution.ui.RunContentDescriptor;
|
||||
import com.intellij.notification.Notification;
|
||||
import com.intellij.notification.NotificationType;
|
||||
import com.intellij.notification.Notifications;
|
||||
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.Installer;
|
||||
import com.jetbrains.cidr.execution.TrivialRunParameters;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.val;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
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)
|
||||
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));
|
||||
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) {
|
||||
//Assume that getDebugParameters reports the bug in a notification already
|
||||
return null;
|
||||
}
|
||||
val manager = XDebuggerManager.getInstance(project);
|
||||
return manager.startSession(environment,
|
||||
new XDebugProcessStarter() {
|
||||
@Override
|
||||
public @NotNull XDebugProcess start(@NotNull XDebugSession session) throws ExecutionException {
|
||||
val process = new ZigLocalDebugProcess(runParameters, session, state.getConsoleBuilder());
|
||||
ProcessTerminatedListener.attach(process.getProcessHandler(), environment.getProject());
|
||||
process.start();
|
||||
return process;
|
||||
}
|
||||
}).getRunContentDescriptor();
|
||||
|
||||
if (runParameters.isRight()) {
|
||||
return startSession(environment, new ErrorProcessStarter(state, runParameters.getRight(), debuggerDriver));
|
||||
} else if (runParameters.isLeft()) {
|
||||
return startSession(environment, new ZigLocalDebugProcessStarter(runParameters.getLeft(), state, environment));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
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.execution.binary.ProfileStateBinary;
|
||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||
|
@ -29,25 +30,14 @@ import org.jetbrains.annotations.NotNull;
|
|||
import java.io.File;
|
||||
|
||||
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);
|
||||
executableFile = profileState.configuration().getExePath().getPathOrThrow().toFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Installer getInstaller() {
|
||||
return new Installer() {
|
||||
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;
|
||||
}
|
||||
};
|
||||
return new ZigDebugEmitBinaryInstaller<>(profileState, toolchain, executableFile, profileState.configuration().getArgs().args);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import com.falsepattern.zigbrains.debugger.execution.binary.ProfileStateBinary;
|
|||
import com.falsepattern.zigbrains.debugger.execution.binary.ZigExecConfigBinary;
|
||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||
import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.RunProfile;
|
||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||
import com.intellij.notification.Notification;
|
||||
|
@ -46,12 +47,8 @@ public class ZigDebugRunnerBinary extends ZigDebugRunnerBase<ProfileStateBinary>
|
|||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ZigDebugParametersBase<ProfileStateBinary> getDebugParameters(ProfileStateBinary profileStateBinary, ExecutionEnvironment environment, DebuggerDriverConfiguration debuggerDriver, AbstractZigToolchain toolchain$) {
|
||||
if (!(toolchain$ instanceof LocalZigToolchain toolchain)) {
|
||||
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);
|
||||
protected @Nullable ZigDebugParametersBase<ProfileStateBinary> getDebugParameters(ProfileStateBinary profileStateBinary, ExecutionEnvironment environment, DebuggerDriverConfiguration debuggerDriver, AbstractZigToolchain toolchain) throws ExecutionException {
|
||||
return new ZigDebugParametersBinary(debuggerDriver, LocalZigToolchain.ensureLocal(toolchain), profileStateBinary);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
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.project.execution.build.ProfileStateBuild;
|
||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||
|
@ -37,74 +38,57 @@ import java.util.Arrays;
|
|||
|
||||
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!";
|
||||
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);
|
||||
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
|
||||
public @NotNull Installer getInstaller() {
|
||||
return new Installer() {
|
||||
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;
|
||||
}
|
||||
};
|
||||
return new ZigDebugEmitBinaryInstaller<>(profileState, toolchain, executableFile, profileState.configuration().getExeArgs().args);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,12 +23,14 @@ import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
|||
import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain;
|
||||
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugParametersBase;
|
||||
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugRunnerBase;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.RunProfile;
|
||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||
import com.intellij.notification.Notification;
|
||||
import com.intellij.notification.NotificationType;
|
||||
import com.intellij.notification.Notifications;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
@ -46,12 +48,9 @@ public class ZigDebugRunnerBuild extends ZigDebugRunnerBase<ProfileStateBuild> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ZigDebugParametersBase<ProfileStateBuild> getDebugParameters(ProfileStateBuild profileStateBuild, ExecutionEnvironment environment, DebuggerDriverConfiguration debuggerDriver, AbstractZigToolchain toolchain$) {
|
||||
if (!(toolchain$ instanceof LocalZigToolchain toolchain)) {
|
||||
Notifications.Bus.notify(new Notification("ZigBrains.Debugger.Error", "The debugger only supports local zig toolchains!", NotificationType.ERROR));
|
||||
return null;
|
||||
}
|
||||
return new ZigDebugParametersBuild(debuggerDriver, toolchain, profileStateBuild);
|
||||
protected @Nullable ZigDebugParametersBase<ProfileStateBuild> getDebugParameters(ProfileStateBuild profileStateBuild, ExecutionEnvironment environment, DebuggerDriverConfiguration debuggerDriver, AbstractZigToolchain toolchain) throws
|
||||
ExecutionException {
|
||||
return new ZigDebugParametersBuild(debuggerDriver, LocalZigToolchain.ensureLocal(toolchain), profileStateBuild);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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.ZigDebugParametersBase;
|
||||
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugParametersEmitBinaryBase;
|
||||
import com.falsepattern.zigbrains.project.execution.run.ProfileStateRun;
|
||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.jetbrains.cidr.execution.Installer;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ZigDebugParametersRun extends ZigDebugParametersBase<ProfileStateRun> {
|
||||
public ZigDebugParametersRun(DebuggerDriverConfiguration driverConfiguration, AbstractZigToolchain toolchain, ProfileStateRun profileState) {
|
||||
super(driverConfiguration, toolchain, profileState);
|
||||
public class ZigDebugParametersRun extends ZigDebugParametersEmitBinaryBase<ProfileStateRun> {
|
||||
public ZigDebugParametersRun(DebuggerDriverConfiguration driverConfiguration, AbstractZigToolchain toolchain, ProfileStateRun profileState)
|
||||
throws ExecutionException {
|
||||
super(driverConfiguration, toolchain, profileState, "run");
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Installer getInstaller() {
|
||||
return new ZigDebugEmitBinaryInstaller<>("run", profileState, toolchain) {
|
||||
@Override
|
||||
public String[] getExeArgs() {
|
||||
return profileState.configuration().getExeArgs().args;
|
||||
}
|
||||
};
|
||||
return new ZigDebugEmitBinaryInstaller<>(profileState, toolchain, executableFile, profileState.configuration().getExeArgs().args);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,12 +16,14 @@
|
|||
|
||||
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.project.execution.base.ProfileStateBase;
|
||||
import com.falsepattern.zigbrains.project.execution.run.ProfileStateRun;
|
||||
import com.falsepattern.zigbrains.project.execution.run.ZigExecConfigRun;
|
||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||
import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.RunProfile;
|
||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||
import com.intellij.notification.Notification;
|
||||
|
@ -45,12 +47,9 @@ public class ZigDebugRunnerRun extends ZigDebugRunnerBase<ProfileStateRun> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected ZigDebugParametersRun getDebugParameters(ProfileStateRun profileStateRun, ExecutionEnvironment environment, DebuggerDriverConfiguration debuggerDriver, AbstractZigToolchain toolchain$) {
|
||||
if (!(toolchain$ instanceof LocalZigToolchain toolchain)) {
|
||||
Notifications.Bus.notify(new Notification("ZigBrains.Debugger.Error", "The debugger only supports local zig toolchains!", NotificationType.ERROR));
|
||||
return null;
|
||||
}
|
||||
return new ZigDebugParametersRun(debuggerDriver, toolchain, profileStateRun);
|
||||
protected ZigDebugParametersRun getDebugParameters(ProfileStateRun profileStateRun, ExecutionEnvironment environment, DebuggerDriverConfiguration debuggerDriver, AbstractZigToolchain toolchain) throws
|
||||
ExecutionException {
|
||||
return new ZigDebugParametersRun(debuggerDriver, LocalZigToolchain.ensureLocal(toolchain), profileStateRun);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,26 +16,24 @@
|
|||
|
||||
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.toolchain.AbstractZigToolchain;
|
||||
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugEmitBinaryInstaller;
|
||||
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugParametersBase;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.jetbrains.cidr.execution.Installer;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ZigDebugParametersTest extends ZigDebugParametersBase<ProfileStateTest> {
|
||||
public ZigDebugParametersTest(DebuggerDriverConfiguration driverConfiguration, AbstractZigToolchain toolchain, ProfileStateTest profileState) {
|
||||
super(driverConfiguration, toolchain, profileState);
|
||||
public class ZigDebugParametersTest extends ZigDebugParametersEmitBinaryBase<ProfileStateTest> {
|
||||
public ZigDebugParametersTest(DebuggerDriverConfiguration driverConfiguration, AbstractZigToolchain toolchain, ProfileStateTest profileState)
|
||||
throws ExecutionException {
|
||||
super(driverConfiguration, toolchain, profileState, "test");
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Installer getInstaller() {
|
||||
return new ZigDebugEmitBinaryInstaller<>("test", profileState, toolchain) {
|
||||
@Override
|
||||
public String[] getExeArgs() {
|
||||
return new String[0];
|
||||
}
|
||||
};
|
||||
return new ZigDebugEmitBinaryInstaller<>(profileState, toolchain, executableFile, new String[0]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
|||
import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain;
|
||||
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugParametersBase;
|
||||
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugRunnerBase;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.RunProfile;
|
||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||
import com.intellij.notification.Notification;
|
||||
|
@ -46,12 +47,9 @@ public class ZigDebugRunnerTest extends ZigDebugRunnerBase<ProfileStateTest> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ZigDebugParametersBase<ProfileStateTest> getDebugParameters(ProfileStateTest profileStateTest, ExecutionEnvironment environment, DebuggerDriverConfiguration debuggerDriver, AbstractZigToolchain toolchain$) {
|
||||
if (!(toolchain$ instanceof LocalZigToolchain toolchain)) {
|
||||
Notifications.Bus.notify(new Notification("ZigBrains.Debugger.Error", "The debugger only supports local zig toolchains!", NotificationType.ERROR));
|
||||
return null;
|
||||
}
|
||||
return new ZigDebugParametersTest(debuggerDriver, toolchain, profileStateTest);
|
||||
protected @Nullable ZigDebugParametersBase<ProfileStateTest> getDebugParameters(ProfileStateTest profileStateTest, ExecutionEnvironment environment, DebuggerDriverConfiguration debuggerDriver, AbstractZigToolchain toolchain) throws
|
||||
ExecutionException {
|
||||
return new ZigDebugParametersTest(debuggerDriver, LocalZigToolchain.ensureLocal(toolchain), profileStateTest);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -64,7 +64,7 @@ public class ZigExecConfigBuild extends ZigExecConfigBase<ZigExecConfigBuild> {
|
|||
steps = truncatedSteps;
|
||||
}
|
||||
result.addAll(steps);
|
||||
result.addAll(CLIUtil.colored(colored.value));
|
||||
result.addAll(CLIUtil.colored(colored.value, debug));
|
||||
result.addAll(List.of(extraArgs.args));
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ public class ZigExecConfigRun extends ZigExecConfigBase<ZigExecConfigRun> {
|
|||
public List<String> buildCommandLineArgs(boolean debug) {
|
||||
val result = new ArrayList<String>();
|
||||
result.add("run");
|
||||
result.addAll(CLIUtil.colored(colored.value));
|
||||
result.addAll(CLIUtil.colored(colored.value, debug));
|
||||
result.add(filePath.getPathOrThrow().toString());
|
||||
if (!debug || optimization.forced) {
|
||||
result.addAll(List.of("-O", optimization.level.name()));
|
||||
|
|
|
@ -46,7 +46,7 @@ public class ZigExecConfigTest extends ZigExecConfigBase<ZigExecConfigTest> {
|
|||
public List<String> buildCommandLineArgs(boolean debug) {
|
||||
val result = new ArrayList<String>();
|
||||
result.add("test");
|
||||
result.addAll(CLIUtil.colored(colored.value));
|
||||
result.addAll(CLIUtil.colored(colored.value, debug));
|
||||
result.add(filePath.getPathOrThrow().toString());
|
||||
if (!debug || optimization.forced) {
|
||||
result.addAll(List.of("-O", optimization.level.name()));
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package com.falsepattern.zigbrains.project.toolchain;
|
||||
|
||||
import com.falsepattern.zigbrains.common.util.PathUtil;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
@ -40,4 +41,11 @@ public class LocalZigToolchain extends AbstractZigToolchain{
|
|||
public Path pathToExecutable(String 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import lombok.val;
|
|||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.StringTokenizer;
|
||||
|
@ -121,7 +122,13 @@ public class CLIUtil {
|
|||
return result.toArray(new String[0]);
|
||||
}
|
||||
|
||||
public static List<String> colored(boolean colored) {
|
||||
return List.of("--color", colored ? "on" : "off");
|
||||
public static List<String> colored(boolean colored, boolean debug) {
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package com.falsepattern.zigbrains.zig.lsp;
|
||||
|
||||
import com.falsepattern.zigbrains.common.util.ApplicationUtil;
|
||||
import com.falsepattern.zigbrains.common.util.StringUtil;
|
||||
import com.falsepattern.zigbrains.lsp.IntellijLanguageClient;
|
||||
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.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class ZLSStartupActivity implements ProjectActivity {
|
||||
|
@ -76,7 +78,8 @@ public class ZLSStartupActivity implements ProjectActivity {
|
|||
val tmpFile = Files.createTempFile("zigbrains-zls-autoconf", ".json");
|
||||
val config = ZLSConfigProvider.findEnvironment(project);
|
||||
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;
|
||||
break blk;
|
||||
}
|
||||
|
@ -172,6 +175,9 @@ public class ZLSStartupActivity implements ProjectActivity {
|
|||
|
||||
if (zlsPath == null) {
|
||||
//Project creation
|
||||
ApplicationUtil.pool(() -> {
|
||||
initZLS(project);
|
||||
}, 5, TimeUnit.SECONDS);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue