feat!: Huge debugging refactor
- Debug support for Windows - Debuggable zig build - Debuggable binaries This is a squashed commit so ignore the weird author date
This commit is contained in:
parent
b4539c0aa9
commit
3d0dbb8e36
52 changed files with 4198 additions and 383 deletions
|
@ -174,6 +174,9 @@ project(":debugger") {
|
|||
dependencies {
|
||||
implementation(project(":zig"))
|
||||
implementation(project(":project"))
|
||||
implementation(project(":common"))
|
||||
implementation(project(":lsp-common"))
|
||||
implementation("org.eclipse.lsp4j:org.eclipse.lsp4j.debug:0.22.0")
|
||||
}
|
||||
intellij {
|
||||
version = clionVersion
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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.common.util;
|
||||
|
||||
import lombok.val;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class CollectionUtil {
|
||||
public static <T> List<T> concat(List<T> a, List<T> b) {
|
||||
val res = new ArrayList<>(a);
|
||||
res.addAll(b);
|
||||
return res;
|
||||
}
|
||||
|
||||
public static <T> List<T> concat(T[] a, List<T> b) {
|
||||
val res = new ArrayList<>(List.of(a));
|
||||
res.addAll(b);
|
||||
return res;
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static <T> List<T> concat(List<T> a, T... b) {
|
||||
val res = new ArrayList<>(a);
|
||||
res.addAll(List.of(b));
|
||||
return res;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T[] concat(T[]... arrays) {
|
||||
if (null != arrays && 0 != arrays.length) {
|
||||
int resultLength = (Integer)java.util.Arrays.stream(arrays).filter(Objects::nonNull).map((e) -> {
|
||||
return e.length;
|
||||
}).reduce(0, Integer::sum);
|
||||
T[] resultArray = (T[]) Array.newInstance(arrays[0].getClass().getComponentType(), resultLength);
|
||||
int i = 0;
|
||||
int n = arrays.length;
|
||||
|
||||
for(int curr = 0; i < n; ++i) {
|
||||
T[] array = arrays[i];
|
||||
if (null != array) {
|
||||
int length = array.length;
|
||||
System.arraycopy(array, 0, resultArray, curr, length);
|
||||
curr += length;
|
||||
}
|
||||
}
|
||||
|
||||
return resultArray;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -40,6 +40,15 @@ public class TextFieldUtil {
|
|||
onTextChanged);
|
||||
}
|
||||
|
||||
public static TextFieldWithBrowseButton pathToFileTextField(Disposable disposable,
|
||||
@NlsContexts.DialogTitle String dialogTitle,
|
||||
Runnable onTextChanged) {
|
||||
return pathTextField(FileChooserDescriptorFactory.createSingleFileDescriptor(),
|
||||
disposable,
|
||||
dialogTitle,
|
||||
onTextChanged);
|
||||
}
|
||||
|
||||
public static TextFieldWithBrowseButton pathTextField(FileChooserDescriptor fileChooserDescriptor,
|
||||
Disposable disposable,
|
||||
@NlsContexts.DialogTitle String dialogTitle,
|
||||
|
|
1
modules/debugger/.gitignore
vendored
Normal file
1
modules/debugger/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
!src/**/build/
|
|
@ -17,10 +17,12 @@
|
|||
package com.falsepattern.zigbrains.zig.debugger;
|
||||
|
||||
import com.falsepattern.zigbrains.zig.debugbridge.DebuggerDriverProvider;
|
||||
import com.falsepattern.zigbrains.zig.debugger.win.WinDebuggerDriverConfiguration;
|
||||
import com.intellij.notification.Notification;
|
||||
import com.intellij.notification.NotificationType;
|
||||
import com.intellij.notification.Notifications;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.util.system.OS;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.lldb.LLDBDriverConfiguration;
|
||||
import lombok.val;
|
||||
|
@ -28,6 +30,9 @@ import org.jetbrains.annotations.Nullable;
|
|||
|
||||
public class Utils {
|
||||
public static @Nullable DebuggerDriverConfiguration getDebuggerConfiguration(Project project) {
|
||||
if (OS.CURRENT == OS.Windows) {
|
||||
return new WinDebuggerDriverConfiguration();
|
||||
}
|
||||
val providedDebugger = DebuggerDriverProvider.findDebuggerConfigurations(project)
|
||||
.filter(x -> x instanceof DebuggerDriverConfiguration)
|
||||
.map(x -> (DebuggerDriverConfiguration)x)
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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.zig.debugger.dap;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
|
||||
class BlockingPipedInputStream extends PipedInputStream {
|
||||
boolean closed;
|
||||
|
||||
public BlockingPipedInputStream(PipedOutputStream pout, int pipeSize) throws IOException {
|
||||
super(pout, pipeSize);
|
||||
}
|
||||
|
||||
public synchronized int read() throws IOException {
|
||||
if (this.closed) {
|
||||
throw new IOException("stream closed");
|
||||
} else {
|
||||
while(super.in < 0) {
|
||||
this.notifyAll();
|
||||
|
||||
try {
|
||||
this.wait(750L);
|
||||
} catch (InterruptedException var2) {
|
||||
throw new InterruptedIOException();
|
||||
}
|
||||
}
|
||||
|
||||
int ret = this.buffer[super.out++] & 255;
|
||||
if (super.out >= this.buffer.length) {
|
||||
super.out = 0;
|
||||
}
|
||||
|
||||
if (super.in == super.out) {
|
||||
super.in = -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
this.closed = true;
|
||||
super.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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.zig.debugger.dap;
|
||||
|
||||
import com.falsepattern.zigbrains.zig.ZigLanguage;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.openapi.util.Expirable;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.UserDataHolderEx;
|
||||
import com.jetbrains.cidr.ArchitectureType;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.DebuggerCommandException;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriver;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.EvaluationContext;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.LLFrame;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.LLThread;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.LLValue;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.LLValueData;
|
||||
import org.eclipse.lsp4j.debug.InitializeRequestArguments;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class DAPDebuggerDriverConfiguration extends DebuggerDriverConfiguration {
|
||||
@Override
|
||||
public abstract @NotNull String getDriverName();
|
||||
|
||||
@Override
|
||||
public abstract @NotNull DebuggerDriver createDriver(@NotNull DebuggerDriver.Handler handler,
|
||||
@NotNull ArchitectureType architectureType) throws ExecutionException;
|
||||
|
||||
public abstract void customizeInitializeArguments(InitializeRequestArguments initArgs);
|
||||
|
||||
@Override
|
||||
public @NotNull Language getConsoleLanguage() {
|
||||
return ZigLanguage.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EvaluationContext createEvaluationContext(@NotNull DebuggerDriver debuggerDriver, @Nullable Expirable expirable, @NotNull LLThread llThread, @NotNull LLFrame llFrame, @NotNull UserDataHolderEx userDataHolderEx) {
|
||||
return new EvaluationContext(debuggerDriver,expirable,llThread,llFrame,userDataHolderEx) {
|
||||
@Override
|
||||
public @NotNull String convertToRValue(@NotNull LLValueData llValueData, @NotNull Pair<LLValue, String> pair) throws DebuggerCommandException, ExecutionException {
|
||||
return cast(pair.getSecond(), pair.getFirst().getType());
|
||||
}
|
||||
} ;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,267 @@
|
|||
/*
|
||||
* 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.zig.debugger.dap;
|
||||
|
||||
import com.falsepattern.zigbrains.common.util.ApplicationUtil;
|
||||
import com.falsepattern.zigbrains.zig.debugger.ZigDebuggerLanguage;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFileUtil;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.FileLocation;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.LLBreakpoint;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.LLBreakpointLocation;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.LLFrame;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.LLInstruction;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.LLMemoryHunk;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.LLModule;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.LLSymbolOffset;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.LLThread;
|
||||
import com.jetbrains.cidr.execution.debugger.memory.Address;
|
||||
import com.jetbrains.cidr.execution.debugger.memory.AddressRange;
|
||||
import lombok.val;
|
||||
import org.eclipse.lsp4j.debug.Breakpoint;
|
||||
import org.eclipse.lsp4j.debug.DisassembledInstruction;
|
||||
import org.eclipse.lsp4j.debug.Module;
|
||||
import org.eclipse.lsp4j.debug.ReadMemoryResponse;
|
||||
import org.eclipse.lsp4j.debug.Source;
|
||||
import org.eclipse.lsp4j.debug.StackFrame;
|
||||
import org.eclipse.lsp4j.debug.Thread;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class Util {
|
||||
public static LLThread threadJBFromDAP(Thread DAPThread) {
|
||||
return new LLThread(DAPThread.getId(), null, null, DAPThread.getName(), null);
|
||||
}
|
||||
|
||||
public static Thread threadDAPFromJB(LLThread JBThread) {
|
||||
val DAPThread = new Thread();
|
||||
DAPThread.setId((int) JBThread.getId());
|
||||
return DAPThread;
|
||||
}
|
||||
|
||||
public static LLBreakpoint breakpointJBFromDAP(Breakpoint DAPBreakpoint) {
|
||||
val source = DAPBreakpoint.getSource();
|
||||
var sourcePath = source == null ? "": Objects.requireNonNullElseGet(source.getPath(), () -> Objects.requireNonNullElse(source.getOrigin(), "unknown"));
|
||||
sourcePath = toJBPath(sourcePath);
|
||||
return new LLBreakpoint(DAPBreakpoint.getId(), sourcePath, Objects.requireNonNullElse(DAPBreakpoint.getLine(), 0) - 1, null);
|
||||
}
|
||||
|
||||
public static @Nullable LLBreakpointLocation getLocation(Breakpoint DAPBreakpoint) {
|
||||
val ref = DAPBreakpoint.getInstructionReference();
|
||||
if (ref == null)
|
||||
return null;
|
||||
val addr = Long.parseLong(ref.substring(2), 16);
|
||||
FileLocation fl = null;
|
||||
val src = DAPBreakpoint.getSource();
|
||||
if (src != null) {
|
||||
fl = new FileLocation(src.getPath(), DAPBreakpoint.getLine());
|
||||
}
|
||||
return new LLBreakpointLocation(DAPBreakpoint.getId() + "", Address.fromUnsignedLong(addr), fl);
|
||||
}
|
||||
|
||||
public static Breakpoint breakpointDAPFromJB(LLBreakpoint JBBreakpoint) {
|
||||
val DAPBreakpoint = new Breakpoint();
|
||||
DAPBreakpoint.setId(JBBreakpoint.getId());
|
||||
DAPBreakpoint.setLine(JBBreakpoint.getOrigLine() + 1);
|
||||
val source = new Source();
|
||||
source.setPath(JBBreakpoint.getOrigFile());
|
||||
DAPBreakpoint.setSource(source);
|
||||
DAPBreakpoint.setMessage(JBBreakpoint.getCondition());
|
||||
return DAPBreakpoint;
|
||||
}
|
||||
|
||||
public static LLModule moduleJBFromDAP(Module DAPModule) {
|
||||
return new LLModule(toJBPath(DAPModule.getPath()));
|
||||
}
|
||||
|
||||
public static Module moduleDAPFromJB(LLModule JBModule) {
|
||||
val DAPModule = new Module();
|
||||
DAPModule.setPath(toJBPath(JBModule.getPath()));
|
||||
DAPModule.setName(JBModule.getName());
|
||||
return DAPModule;
|
||||
}
|
||||
|
||||
public static LLFrame frameJBFromDAP(StackFrame DAPFrame, @Nullable DAPDriver.MappedBreakpoint helperBreakpoint, Map<Integer, DAPDriver.MappedModule> modules) {
|
||||
val ptr = parseAddress(DAPFrame.getInstructionPointerReference());
|
||||
val name = DAPFrame.getName();
|
||||
boolean inline = name.startsWith("[Inline Frame] ");
|
||||
val function = name.substring(name.indexOf('!') + 1, name.indexOf('('));
|
||||
val moduleID = DAPFrame.getModuleId();
|
||||
String moduleName = null;
|
||||
if (moduleID != null) {
|
||||
if (moduleID.isRight()) {
|
||||
moduleName = moduleID.getRight();
|
||||
} else {
|
||||
val module = modules.get(moduleID.getLeft());
|
||||
moduleName = module.java().getName();
|
||||
}
|
||||
}
|
||||
var line = DAPFrame.getLine();
|
||||
String sourcePath;
|
||||
{
|
||||
val src = DAPFrame.getSource();
|
||||
sourcePath = src == null ? null : toJBPath(src.getPath());
|
||||
}
|
||||
if (helperBreakpoint != null) {
|
||||
if (line == 0) {
|
||||
line = helperBreakpoint.dap().getLine();
|
||||
}
|
||||
if (sourcePath == null) {
|
||||
val src = helperBreakpoint.dap().getSource();
|
||||
if (src != null) {
|
||||
sourcePath = toJBPath(src.getPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
return new LLFrame(DAPFrame.getId(),
|
||||
function,
|
||||
sourcePath,
|
||||
null,
|
||||
line - 1,
|
||||
ptr,
|
||||
ZigDebuggerLanguage.INSTANCE,
|
||||
false,
|
||||
inline,
|
||||
moduleName);
|
||||
}
|
||||
|
||||
public static Source toSource(String path) {
|
||||
val src = new Source();
|
||||
val absolute = Path.of(path).toAbsolutePath();
|
||||
src.setName(absolute.getFileName().toString());
|
||||
src.setPath(toWinPath(absolute.toString()));
|
||||
return src;
|
||||
}
|
||||
|
||||
public static String toWinPath(String path) {
|
||||
if (path == null)
|
||||
return null;
|
||||
return path.replace('/', '\\');
|
||||
}
|
||||
|
||||
public static String toJBPath(String path) {
|
||||
if (path == null)
|
||||
return null;
|
||||
return path.replace('\\', '/');
|
||||
}
|
||||
|
||||
public static Long parseAddressNullable(String address) {
|
||||
if (address == null)
|
||||
return null;
|
||||
return parseAddress(address);
|
||||
}
|
||||
|
||||
public static long parseAddress(String address) {
|
||||
if (address == null)
|
||||
return 0L;
|
||||
if (!address.startsWith("0x"))
|
||||
return Long.parseUnsignedLong(address);
|
||||
return Long.parseUnsignedLong(address.substring(2), 16);
|
||||
}
|
||||
|
||||
public static String stringifyAddress(long address) {
|
||||
return "0x" + Long.toHexString(address);
|
||||
}
|
||||
|
||||
private static final Pattern HEX_FIX_REGEX = Pattern.compile("([0-9A-F]+)(?<!\\W)h");
|
||||
public static LLInstruction instructionJBFromDAP(DisassembledInstruction DAPInstruction, Source loc, Integer startLine, Integer endLine, boolean uniq, LLSymbolOffset symbol) {
|
||||
val address = Address.parseHexString(DAPInstruction.getAddress());
|
||||
val byteStrings = DAPInstruction.getInstructionBytes().split(" ");
|
||||
val bytes = new ArrayList<Byte>(byteStrings.length);
|
||||
for (val byteString: byteStrings) {
|
||||
bytes.add((byte) Integer.parseInt(byteString, 16));
|
||||
}
|
||||
val result = new ArrayList<LLInstruction>();
|
||||
String comment = null;
|
||||
blk:
|
||||
if (loc != null && startLine != null && endLine != null && uniq) {
|
||||
val pathStr = Util.toJBPath(loc.getPath());
|
||||
Path path;
|
||||
try {
|
||||
path = Path.of(pathStr);
|
||||
} catch (InvalidPathException ignored) {
|
||||
break blk;
|
||||
}
|
||||
val text = ApplicationUtil.computableReadAction(() -> {
|
||||
val file = VfsUtil.findFile(path, true);
|
||||
if (file == null)
|
||||
return null;
|
||||
val doc = VirtualFileUtil.findDocument(file);
|
||||
if (doc == null)
|
||||
return null;
|
||||
return doc.getImmutableCharSequence().toString().split("(\r\n|\r|\n)");
|
||||
});
|
||||
if (text == null)
|
||||
break blk;
|
||||
startLine -= 1;
|
||||
endLine -= 1;
|
||||
if (text.length <= endLine)
|
||||
break blk;
|
||||
comment = text[endLine];
|
||||
}
|
||||
var nicerDisassembly = new StringBuilder();
|
||||
var disassembly = DAPInstruction.getInstruction();
|
||||
val matcher = HEX_FIX_REGEX.matcher(disassembly);
|
||||
int prevEnd = 0;
|
||||
while (matcher.find()) {
|
||||
nicerDisassembly.append(disassembly, prevEnd, matcher.start());
|
||||
val hex = matcher.group(1).toLowerCase();
|
||||
nicerDisassembly.append("0x").append(hex);
|
||||
prevEnd = matcher.end();
|
||||
}
|
||||
if (prevEnd < disassembly.length())
|
||||
nicerDisassembly.append(disassembly, prevEnd, disassembly.length());
|
||||
return LLInstruction.create(address,
|
||||
bytes,
|
||||
nicerDisassembly.toString(),
|
||||
comment,
|
||||
symbol);
|
||||
}
|
||||
|
||||
public static LLMemoryHunk memoryJBFromDAP(ReadMemoryResponse DAPMemory) {
|
||||
val address = Util.parseAddress(DAPMemory.getAddress());
|
||||
val bytes = Base64.getDecoder().decode(DAPMemory.getData());
|
||||
val range = new AddressRange(Address.fromUnsignedLong(address), Address.fromUnsignedLong(address + bytes.length - 1));
|
||||
return new LLMemoryHunk(range, bytes);
|
||||
}
|
||||
|
||||
public static <T> T get(CompletableFuture<T> future) throws ExecutionException {
|
||||
try {
|
||||
return future.get(4, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException | TimeoutException e) {
|
||||
throw new ExecutionException(e);
|
||||
} catch (java.util.concurrent.ExecutionException e) {
|
||||
throw new ExecutionException(e.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
public static @NotNull String emptyIfNull(@Nullable String str) {
|
||||
return str == null ? "" : str;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,518 @@
|
|||
/*
|
||||
* 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.zig.debugger.dap;
|
||||
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.eclipse.lsp4j.debug.BreakpointLocationsArguments;
|
||||
import org.eclipse.lsp4j.debug.BreakpointLocationsResponse;
|
||||
import org.eclipse.lsp4j.debug.CancelArguments;
|
||||
import org.eclipse.lsp4j.debug.Capabilities;
|
||||
import org.eclipse.lsp4j.debug.CompletionsArguments;
|
||||
import org.eclipse.lsp4j.debug.CompletionsResponse;
|
||||
import org.eclipse.lsp4j.debug.ConfigurationDoneArguments;
|
||||
import org.eclipse.lsp4j.debug.ContinueArguments;
|
||||
import org.eclipse.lsp4j.debug.ContinueResponse;
|
||||
import org.eclipse.lsp4j.debug.DataBreakpointInfoArguments;
|
||||
import org.eclipse.lsp4j.debug.DataBreakpointInfoResponse;
|
||||
import org.eclipse.lsp4j.debug.DisassembleArguments;
|
||||
import org.eclipse.lsp4j.debug.DisassembleResponse;
|
||||
import org.eclipse.lsp4j.debug.DisconnectArguments;
|
||||
import org.eclipse.lsp4j.debug.EvaluateArguments;
|
||||
import org.eclipse.lsp4j.debug.EvaluateResponse;
|
||||
import org.eclipse.lsp4j.debug.ExceptionInfoArguments;
|
||||
import org.eclipse.lsp4j.debug.ExceptionInfoResponse;
|
||||
import org.eclipse.lsp4j.debug.GotoArguments;
|
||||
import org.eclipse.lsp4j.debug.GotoTargetsArguments;
|
||||
import org.eclipse.lsp4j.debug.GotoTargetsResponse;
|
||||
import org.eclipse.lsp4j.debug.InitializeRequestArguments;
|
||||
import org.eclipse.lsp4j.debug.LoadedSourcesArguments;
|
||||
import org.eclipse.lsp4j.debug.LoadedSourcesResponse;
|
||||
import org.eclipse.lsp4j.debug.ModulesArguments;
|
||||
import org.eclipse.lsp4j.debug.ModulesResponse;
|
||||
import org.eclipse.lsp4j.debug.NextArguments;
|
||||
import org.eclipse.lsp4j.debug.PauseArguments;
|
||||
import org.eclipse.lsp4j.debug.ReadMemoryArguments;
|
||||
import org.eclipse.lsp4j.debug.ReadMemoryResponse;
|
||||
import org.eclipse.lsp4j.debug.RestartArguments;
|
||||
import org.eclipse.lsp4j.debug.RestartFrameArguments;
|
||||
import org.eclipse.lsp4j.debug.ReverseContinueArguments;
|
||||
import org.eclipse.lsp4j.debug.ScopesArguments;
|
||||
import org.eclipse.lsp4j.debug.ScopesResponse;
|
||||
import org.eclipse.lsp4j.debug.SetBreakpointsArguments;
|
||||
import org.eclipse.lsp4j.debug.SetBreakpointsResponse;
|
||||
import org.eclipse.lsp4j.debug.SetDataBreakpointsArguments;
|
||||
import org.eclipse.lsp4j.debug.SetDataBreakpointsResponse;
|
||||
import org.eclipse.lsp4j.debug.SetExceptionBreakpointsArguments;
|
||||
import org.eclipse.lsp4j.debug.SetExceptionBreakpointsResponse;
|
||||
import org.eclipse.lsp4j.debug.SetExpressionArguments;
|
||||
import org.eclipse.lsp4j.debug.SetExpressionResponse;
|
||||
import org.eclipse.lsp4j.debug.SetFunctionBreakpointsArguments;
|
||||
import org.eclipse.lsp4j.debug.SetFunctionBreakpointsResponse;
|
||||
import org.eclipse.lsp4j.debug.SetInstructionBreakpointsArguments;
|
||||
import org.eclipse.lsp4j.debug.SetInstructionBreakpointsResponse;
|
||||
import org.eclipse.lsp4j.debug.SetVariableArguments;
|
||||
import org.eclipse.lsp4j.debug.SetVariableResponse;
|
||||
import org.eclipse.lsp4j.debug.SourceArguments;
|
||||
import org.eclipse.lsp4j.debug.SourceResponse;
|
||||
import org.eclipse.lsp4j.debug.StackTraceArguments;
|
||||
import org.eclipse.lsp4j.debug.StackTraceResponse;
|
||||
import org.eclipse.lsp4j.debug.StepBackArguments;
|
||||
import org.eclipse.lsp4j.debug.StepInArguments;
|
||||
import org.eclipse.lsp4j.debug.StepInTargetsArguments;
|
||||
import org.eclipse.lsp4j.debug.StepInTargetsResponse;
|
||||
import org.eclipse.lsp4j.debug.StepOutArguments;
|
||||
import org.eclipse.lsp4j.debug.TerminateArguments;
|
||||
import org.eclipse.lsp4j.debug.TerminateThreadsArguments;
|
||||
import org.eclipse.lsp4j.debug.ThreadsResponse;
|
||||
import org.eclipse.lsp4j.debug.VariablesArguments;
|
||||
import org.eclipse.lsp4j.debug.VariablesResponse;
|
||||
import org.eclipse.lsp4j.debug.WriteMemoryArguments;
|
||||
import org.eclipse.lsp4j.debug.WriteMemoryResponse;
|
||||
import org.eclipse.lsp4j.debug.services.IDebugProtocolServer;
|
||||
import org.eclipse.lsp4j.jsonrpc.services.JsonRequest;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static com.falsepattern.zigbrains.zig.debugger.dap.Util.get;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@RequiredArgsConstructor
|
||||
public class WrappedDebugServer<T extends IDebugProtocolServer> implements IDebugProtocolServer {
|
||||
protected final T server;
|
||||
|
||||
public void cancelNow(CancelArguments args) throws ExecutionException {
|
||||
get(cancel(args));
|
||||
}
|
||||
|
||||
public Capabilities initializeNow(InitializeRequestArguments args) throws ExecutionException {
|
||||
return get(initialize(args));
|
||||
}
|
||||
|
||||
public void configurationDoneNow(ConfigurationDoneArguments args) throws ExecutionException {
|
||||
get(configurationDone(args));
|
||||
}
|
||||
|
||||
public void launchNow(Map<String, Object> args) throws ExecutionException {
|
||||
get(launch(args));
|
||||
}
|
||||
|
||||
public void attachNow(Map<String, Object> args) throws ExecutionException {
|
||||
get(attach(args));
|
||||
}
|
||||
|
||||
public void restartNow(RestartArguments args) throws ExecutionException {
|
||||
get(restart(args));
|
||||
}
|
||||
|
||||
public void disconnectNow(DisconnectArguments args) throws ExecutionException {
|
||||
get(disconnect(args));
|
||||
}
|
||||
|
||||
public void terminateNow(TerminateArguments args) throws ExecutionException {
|
||||
get(terminate(args));
|
||||
}
|
||||
|
||||
public BreakpointLocationsResponse breakpointLocationsNow(BreakpointLocationsArguments args) throws ExecutionException {
|
||||
return get(breakpointLocations(args));
|
||||
}
|
||||
|
||||
public SetBreakpointsResponse setBreakpointsNow(SetBreakpointsArguments args) throws ExecutionException {
|
||||
return get(setBreakpoints(args));
|
||||
}
|
||||
|
||||
public SetFunctionBreakpointsResponse setFunctionBreakpointsNow(SetFunctionBreakpointsArguments args) throws ExecutionException {
|
||||
return get(setFunctionBreakpoints(args));
|
||||
}
|
||||
|
||||
public SetExceptionBreakpointsResponse setExceptionBreakpointsNow(SetExceptionBreakpointsArguments args) throws ExecutionException {
|
||||
return get(setExceptionBreakpoints(args));
|
||||
}
|
||||
|
||||
public DataBreakpointInfoResponse dataBreakpointInfoNow(DataBreakpointInfoArguments args) throws ExecutionException {
|
||||
return get(dataBreakpointInfo(args));
|
||||
}
|
||||
|
||||
public SetDataBreakpointsResponse setDataBreakpointsNow(SetDataBreakpointsArguments args) throws ExecutionException {
|
||||
return get(setDataBreakpoints(args));
|
||||
}
|
||||
|
||||
public SetInstructionBreakpointsResponse setInstructionBreakpointsNow(SetInstructionBreakpointsArguments args) throws ExecutionException {
|
||||
return get(setInstructionBreakpoints(args));
|
||||
}
|
||||
|
||||
public ContinueResponse continueNow(ContinueArguments args) throws ExecutionException {
|
||||
return get(continue_(args));
|
||||
}
|
||||
|
||||
public void nextNow(NextArguments args) throws ExecutionException {
|
||||
get(next(args));
|
||||
}
|
||||
|
||||
public void stepInNow(StepInArguments args) throws ExecutionException {
|
||||
get(stepIn(args));
|
||||
}
|
||||
|
||||
public void stepOutNow(StepOutArguments args) throws ExecutionException {
|
||||
get(stepOut(args));
|
||||
}
|
||||
|
||||
public void stepBackNow(StepBackArguments args) throws ExecutionException {
|
||||
get(stepBack(args));
|
||||
}
|
||||
|
||||
public void reverseContinueNow(ReverseContinueArguments args) throws ExecutionException {
|
||||
get(reverseContinue(args));
|
||||
}
|
||||
|
||||
public void restartFrameNow(RestartFrameArguments args) throws ExecutionException {
|
||||
get(restartFrame(args));
|
||||
}
|
||||
|
||||
public void gotoNow(GotoArguments args) throws ExecutionException {
|
||||
get(goto_(args));
|
||||
}
|
||||
|
||||
public void pauseNow(PauseArguments args) throws ExecutionException {
|
||||
get(pause(args));
|
||||
}
|
||||
|
||||
public StackTraceResponse stackTraceNow(StackTraceArguments args) throws ExecutionException {
|
||||
return get(stackTrace(args));
|
||||
}
|
||||
|
||||
public ScopesResponse scopesNow(ScopesArguments args) throws ExecutionException {
|
||||
return get(scopes(args));
|
||||
}
|
||||
|
||||
public VariablesResponse variablesNow(VariablesArguments args) throws ExecutionException {
|
||||
return get(variables(args));
|
||||
}
|
||||
|
||||
public SetVariableResponse setVariableNow(SetVariableArguments args) throws ExecutionException {
|
||||
return get(setVariable(args));
|
||||
}
|
||||
|
||||
public SourceResponse sourceNow(SourceArguments args) throws ExecutionException {
|
||||
return get(source(args));
|
||||
}
|
||||
|
||||
public ThreadsResponse threadsNow() throws ExecutionException {
|
||||
return get(threads());
|
||||
}
|
||||
|
||||
public void terminateThreadsNow(TerminateThreadsArguments args) throws ExecutionException {
|
||||
get(terminateThreads(args));
|
||||
}
|
||||
|
||||
public ModulesResponse modulesNow(ModulesArguments args) throws ExecutionException {
|
||||
return get(modules(args));
|
||||
}
|
||||
|
||||
public LoadedSourcesResponse loadedSourcesNow(LoadedSourcesArguments args) throws ExecutionException {
|
||||
return get(loadedSources(args));
|
||||
}
|
||||
|
||||
public EvaluateResponse evaluateNow(EvaluateArguments args) throws ExecutionException {
|
||||
return get(evaluate(args));
|
||||
}
|
||||
|
||||
public SetExpressionResponse setExpressionNow(SetExpressionArguments args) throws ExecutionException {
|
||||
return get(setExpression(args));
|
||||
}
|
||||
|
||||
public StepInTargetsResponse stepInTargetsNow(StepInTargetsArguments args) throws ExecutionException {
|
||||
return get(stepInTargets(args));
|
||||
}
|
||||
|
||||
public GotoTargetsResponse gotoTargetsNow(GotoTargetsArguments args) throws ExecutionException {
|
||||
return get(gotoTargets(args));
|
||||
}
|
||||
|
||||
public CompletionsResponse completionsNow(CompletionsArguments args) throws ExecutionException {
|
||||
return get(completions(args));
|
||||
}
|
||||
|
||||
public ExceptionInfoResponse exceptionInfoNow(ExceptionInfoArguments args) throws ExecutionException {
|
||||
return get(exceptionInfo(args));
|
||||
}
|
||||
|
||||
public ReadMemoryResponse readMemoryNow(ReadMemoryArguments args) throws ExecutionException {
|
||||
return get(readMemory(args));
|
||||
}
|
||||
|
||||
public WriteMemoryResponse writeMemoryNow(WriteMemoryArguments args) throws ExecutionException {
|
||||
return get(writeMemory(args));
|
||||
}
|
||||
|
||||
public DisassembleResponse disassembleNow(DisassembleArguments args) throws ExecutionException {
|
||||
return get(disassemble(args));
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<Void> cancel(CancelArguments args) {
|
||||
return server.cancel(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<Capabilities> initialize(InitializeRequestArguments args) {
|
||||
return server.initialize(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<Void> configurationDone(ConfigurationDoneArguments args) {
|
||||
return server.configurationDone(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<Void> launch(Map<String, Object> args) {
|
||||
return server.launch(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<Void> attach(Map<String, Object> args) {
|
||||
return server.attach(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<Void> restart(RestartArguments args) {
|
||||
return server.restart(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<Void> disconnect(DisconnectArguments args) {
|
||||
return server.disconnect(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<Void> terminate(TerminateArguments args) {
|
||||
return server.terminate(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<BreakpointLocationsResponse> breakpointLocations(BreakpointLocationsArguments args) {
|
||||
return server.breakpointLocations(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<SetBreakpointsResponse> setBreakpoints(SetBreakpointsArguments args) {
|
||||
return server.setBreakpoints(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<SetFunctionBreakpointsResponse> setFunctionBreakpoints(SetFunctionBreakpointsArguments args) {
|
||||
return server.setFunctionBreakpoints(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<SetExceptionBreakpointsResponse> setExceptionBreakpoints(SetExceptionBreakpointsArguments args) {
|
||||
return server.setExceptionBreakpoints(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<DataBreakpointInfoResponse> dataBreakpointInfo(DataBreakpointInfoArguments args) {
|
||||
return server.dataBreakpointInfo(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<SetDataBreakpointsResponse> setDataBreakpoints(SetDataBreakpointsArguments args) {
|
||||
return server.setDataBreakpoints(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<SetInstructionBreakpointsResponse> setInstructionBreakpoints(SetInstructionBreakpointsArguments args) {
|
||||
return server.setInstructionBreakpoints(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest("continue")
|
||||
public CompletableFuture<ContinueResponse> continue_(ContinueArguments args) {
|
||||
return server.continue_(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<Void> next(NextArguments args) {
|
||||
return server.next(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<Void> stepIn(StepInArguments args) {
|
||||
return server.stepIn(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<Void> stepOut(StepOutArguments args) {
|
||||
return server.stepOut(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<Void> stepBack(StepBackArguments args) {
|
||||
return server.stepBack(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<Void> reverseContinue(ReverseContinueArguments args) {
|
||||
return server.reverseContinue(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<Void> restartFrame(RestartFrameArguments args) {
|
||||
return server.restartFrame(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest("goto")
|
||||
public CompletableFuture<Void> goto_(GotoArguments args) {
|
||||
return server.goto_(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<Void> pause(PauseArguments args) {
|
||||
return server.pause(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<StackTraceResponse> stackTrace(StackTraceArguments args) {
|
||||
return server.stackTrace(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<ScopesResponse> scopes(ScopesArguments args) {
|
||||
return server.scopes(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<VariablesResponse> variables(VariablesArguments args) {
|
||||
return server.variables(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<SetVariableResponse> setVariable(SetVariableArguments args) {
|
||||
return server.setVariable(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<SourceResponse> source(SourceArguments args) {
|
||||
return server.source(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<ThreadsResponse> threads() {
|
||||
return server.threads();
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<Void> terminateThreads(TerminateThreadsArguments args) {
|
||||
return server.terminateThreads(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<ModulesResponse> modules(ModulesArguments args) {
|
||||
return server.modules(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<LoadedSourcesResponse> loadedSources(LoadedSourcesArguments args) {
|
||||
return server.loadedSources(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<EvaluateResponse> evaluate(EvaluateArguments args) {
|
||||
return server.evaluate(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<SetExpressionResponse> setExpression(SetExpressionArguments args) {
|
||||
return server.setExpression(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<StepInTargetsResponse> stepInTargets(StepInTargetsArguments args) {
|
||||
return server.stepInTargets(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<GotoTargetsResponse> gotoTargets(GotoTargetsArguments args) {
|
||||
return server.gotoTargets(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<CompletionsResponse> completions(CompletionsArguments args) {
|
||||
return server.completions(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<ExceptionInfoResponse> exceptionInfo(ExceptionInfoArguments args) {
|
||||
return server.exceptionInfo(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<ReadMemoryResponse> readMemory(ReadMemoryArguments args) {
|
||||
return server.readMemory(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<WriteMemoryResponse> writeMemory(WriteMemoryArguments args) {
|
||||
return server.writeMemory(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonRequest
|
||||
public CompletableFuture<DisassembleResponse> disassemble(DisassembleArguments args) {
|
||||
return server.disassemble(args);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* 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.zig.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;
|
||||
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;
|
||||
protected final ProfileState profileState;
|
||||
protected final AbstractZigToolchain toolchain;
|
||||
private File executableFile;
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
//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.withCharset(StandardCharsets.UTF_8);
|
||||
cli.withRedirectErrorStream(true);
|
||||
return cli;
|
||||
}
|
||||
|
||||
public abstract String[] getExeArgs();
|
||||
|
||||
@Override
|
||||
public @NotNull File getExecutableFile() {
|
||||
return executableFile;
|
||||
}
|
||||
}
|
|
@ -14,10 +14,12 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.falsepattern.zigbrains.zig.debugger.base;
|
||||
package com.falsepattern.zigbrains.zig.debugger.runner.base;
|
||||
|
||||
import com.falsepattern.zigbrains.project.execution.base.ProfileStateBase;
|
||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||
import com.intellij.util.system.CpuArch;
|
||||
import com.jetbrains.cidr.ArchitectureType;
|
||||
import com.jetbrains.cidr.execution.RunParameters;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
@ -38,6 +40,6 @@ public abstract class ZigDebugParametersBase<ProfileState extends ProfileStateBa
|
|||
|
||||
@Override
|
||||
public @Nullable String getArchitectureId() {
|
||||
return null;
|
||||
return ArchitectureType.forVmCpuArch(CpuArch.CURRENT).getId();
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.falsepattern.zigbrains.zig.debugger.base;
|
||||
package com.falsepattern.zigbrains.zig.debugger.runner.base;
|
||||
|
||||
import com.falsepattern.zigbrains.project.execution.base.ProfileStateBase;
|
||||
import com.falsepattern.zigbrains.project.runconfig.ZigProgramRunnerBase;
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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.zig.debugger.runner.binary;
|
||||
|
||||
import com.falsepattern.zigbrains.project.execution.binary.ProfileStateBinary;
|
||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||
import com.falsepattern.zigbrains.zig.debugger.runner.base.ZigDebugParametersBase;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.jetbrains.cidr.execution.Installer;
|
||||
import com.jetbrains.cidr.execution.TrivialInstaller;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class ZigDebugParametersBinary extends ZigDebugParametersBase<ProfileStateBinary> {
|
||||
public ZigDebugParametersBinary(DebuggerDriverConfiguration driverConfiguration, AbstractZigToolchain toolchain, ProfileStateBinary profileStateBinary) {
|
||||
super(driverConfiguration, toolchain, profileStateBinary);
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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.zig.debugger.runner.binary;
|
||||
|
||||
import com.falsepattern.zigbrains.project.execution.base.ProfileStateBase;
|
||||
import com.falsepattern.zigbrains.project.execution.binary.ProfileStateBinary;
|
||||
import com.falsepattern.zigbrains.project.execution.binary.ZigExecConfigBinary;
|
||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||
import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain;
|
||||
import com.falsepattern.zigbrains.zig.debugger.runner.base.ZigDebugParametersBase;
|
||||
import com.falsepattern.zigbrains.zig.debugger.runner.base.ZigDebugRunnerBase;
|
||||
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 org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ZigDebugRunnerBinary extends ZigDebugRunnerBase<ProfileStateBinary> {
|
||||
@Override
|
||||
public @NotNull @NonNls String getRunnerId() {
|
||||
return "ZigDebugRunnerBinary";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRun(@NotNull String executorId, @NotNull RunProfile profile) {
|
||||
return this.executorId.equals(executorId) &&
|
||||
(profile instanceof ZigExecConfigBinary);
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ProfileStateBinary castProfileState(ProfileStateBase<?> state) {
|
||||
return state instanceof ProfileStateBinary state$ ? state$ : null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* 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.zig.debugger.runner.build;
|
||||
|
||||
import com.falsepattern.zigbrains.project.execution.build.ProfileStateBuild;
|
||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||
import com.falsepattern.zigbrains.project.util.CLIUtil;
|
||||
import com.falsepattern.zigbrains.zig.debugger.runner.base.ZigDebugParametersBase;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.jetbrains.cidr.execution.Installer;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class ZigDebugParametersBuild extends ZigDebugParametersBase<ProfileStateBuild> {
|
||||
public ZigDebugParametersBuild(DebuggerDriverConfiguration driverConfiguration, AbstractZigToolchain toolchain, ProfileStateBuild profileStateBuild) {
|
||||
super(driverConfiguration, toolchain, profileStateBuild);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Installer getInstaller() {
|
||||
return new Installer() {
|
||||
private File executableFile;
|
||||
@Override
|
||||
public @NotNull GeneralCommandLine install() throws ExecutionException {
|
||||
val exePath = profileState.configuration().getExePath().getPath();
|
||||
if (exePath.isEmpty()) {
|
||||
throw new ExecutionException("Please specify the output exe path to debug \"zig build\" tasks!");
|
||||
}
|
||||
Path exe = exePath.get();
|
||||
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());
|
||||
}
|
||||
|
||||
if (!Files.exists(exe) || !Files.isExecutable(exe)) {
|
||||
throw new ExecutionException("File " + exe + " does not exist or is not executable!");
|
||||
}
|
||||
|
||||
executableFile = exe.toFile();
|
||||
|
||||
//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.withCharset(StandardCharsets.UTF_8);
|
||||
cli.withRedirectErrorStream(true);
|
||||
return cli;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull File getExecutableFile() {
|
||||
return executableFile;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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.zig.debugger.runner.build;
|
||||
|
||||
import com.falsepattern.zigbrains.project.execution.base.ProfileStateBase;
|
||||
import com.falsepattern.zigbrains.project.execution.build.ProfileStateBuild;
|
||||
import com.falsepattern.zigbrains.project.execution.build.ZigExecConfigBuild;
|
||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||
import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain;
|
||||
import com.falsepattern.zigbrains.zig.debugger.runner.base.ZigDebugParametersBase;
|
||||
import com.falsepattern.zigbrains.zig.debugger.runner.base.ZigDebugRunnerBase;
|
||||
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 org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ZigDebugRunnerBuild extends ZigDebugRunnerBase<ProfileStateBuild> {
|
||||
@Override
|
||||
public @NotNull @NonNls String getRunnerId() {
|
||||
return "ZigDebugRunnerBuild";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRun(@NotNull String executorId, @NotNull RunProfile profile) {
|
||||
return this.executorId.equals(executorId) &&
|
||||
(profile instanceof ZigExecConfigBuild);
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ProfileStateBuild castProfileState(ProfileStateBase<?> state) {
|
||||
return state instanceof ProfileStateBuild state$ ? state$ : null;
|
||||
}
|
||||
}
|
|
@ -14,13 +14,13 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.falsepattern.zigbrains.zig.debugger.run;
|
||||
package com.falsepattern.zigbrains.zig.debugger.runner.run;
|
||||
|
||||
import com.falsepattern.zigbrains.project.execution.run.ProfileStateRun;
|
||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||
import com.falsepattern.zigbrains.zig.debugger.base.ZigDebugParametersBase;
|
||||
import com.falsepattern.zigbrains.zig.debugger.runner.base.ZigDebugEmitBinaryInstaller;
|
||||
import com.falsepattern.zigbrains.zig.debugger.runner.base.ZigDebugParametersBase;
|
||||
import com.jetbrains.cidr.execution.Installer;
|
||||
import com.jetbrains.cidr.execution.TrivialInstaller;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
@ -31,6 +31,11 @@ public class ZigDebugParametersRun extends ZigDebugParametersBase<ProfileStateRu
|
|||
|
||||
@Override
|
||||
public @NotNull Installer getInstaller() {
|
||||
return new TrivialInstaller(profileState.getCommandLine(toolchain));
|
||||
return new ZigDebugEmitBinaryInstaller<>("run", profileState, toolchain) {
|
||||
@Override
|
||||
public String[] getExeArgs() {
|
||||
return profileState.configuration().getExeArgs().args;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -14,14 +14,14 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.falsepattern.zigbrains.zig.debugger.run;
|
||||
package com.falsepattern.zigbrains.zig.debugger.runner.run;
|
||||
|
||||
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.falsepattern.zigbrains.zig.debugger.base.ZigDebugRunnerBase;
|
||||
import com.falsepattern.zigbrains.zig.debugger.runner.base.ZigDebugRunnerBase;
|
||||
import com.intellij.execution.configurations.RunProfile;
|
||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||
import com.intellij.notification.Notification;
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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.zig.debugger.runner.test;
|
||||
|
||||
import com.falsepattern.zigbrains.project.execution.test.ProfileStateTest;
|
||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||
import com.falsepattern.zigbrains.zig.debugger.runner.base.ZigDebugEmitBinaryInstaller;
|
||||
import com.falsepattern.zigbrains.zig.debugger.runner.base.ZigDebugParametersBase;
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Installer getInstaller() {
|
||||
return new ZigDebugEmitBinaryInstaller<>("test", profileState, toolchain) {
|
||||
@Override
|
||||
public String[] getExeArgs() {
|
||||
return new String[0];
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -14,15 +14,15 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.falsepattern.zigbrains.zig.debugger.test;
|
||||
package com.falsepattern.zigbrains.zig.debugger.runner.test;
|
||||
|
||||
import com.falsepattern.zigbrains.project.execution.base.ProfileStateBase;
|
||||
import com.falsepattern.zigbrains.project.execution.test.ProfileStateTest;
|
||||
import com.falsepattern.zigbrains.project.execution.test.ZigExecConfigTest;
|
||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||
import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain;
|
||||
import com.falsepattern.zigbrains.zig.debugger.base.ZigDebugParametersBase;
|
||||
import com.falsepattern.zigbrains.zig.debugger.base.ZigDebugRunnerBase;
|
||||
import com.falsepattern.zigbrains.zig.debugger.runner.base.ZigDebugParametersBase;
|
||||
import com.falsepattern.zigbrains.zig.debugger.runner.base.ZigDebugRunnerBase;
|
||||
import com.intellij.execution.configurations.RunProfile;
|
||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||
import com.intellij.notification.Notification;
|
|
@ -1,100 +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.zig.debugger.test;
|
||||
|
||||
import com.falsepattern.zigbrains.project.execution.test.ProfileStateTest;
|
||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||
import com.falsepattern.zigbrains.project.util.CLIUtil;
|
||||
import com.falsepattern.zigbrains.zig.debugger.base.ZigDebugParametersBase;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.intellij.execution.process.ProcessEvent;
|
||||
import com.intellij.execution.process.ProcessListener;
|
||||
import com.jetbrains.cidr.execution.Installer;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ZigDebugParametersTest extends ZigDebugParametersBase<ProfileStateTest> {
|
||||
public ZigDebugParametersTest(DebuggerDriverConfiguration driverConfiguration, AbstractZigToolchain toolchain, ProfileStateTest profileState) {
|
||||
super(driverConfiguration, toolchain, profileState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Installer getInstaller() {
|
||||
return new ZigTestInstaller();
|
||||
}
|
||||
|
||||
private class ZigTestInstaller implements Installer {
|
||||
private File executableFile;
|
||||
@Override
|
||||
public @NotNull GeneralCommandLine install() throws ExecutionException {
|
||||
val commandLine = profileState.getCommandLine(toolchain);
|
||||
final Path tmpDir;
|
||||
try {
|
||||
tmpDir = Files.createTempDirectory("zigbrains_debug").toAbsolutePath();
|
||||
} catch (IOException e) {
|
||||
throw new ExecutionException("Failed to create temporary directory for test binary", e);
|
||||
}
|
||||
val exe = tmpDir.resolve("executable").toFile();
|
||||
commandLine.addParameters("--test-no-exec", "-femit-bin=" + exe.getAbsolutePath());
|
||||
val outputOpt = CLIUtil.execute(commandLine, Integer.MAX_VALUE);
|
||||
if (outputOpt.isEmpty()) {
|
||||
throw new ExecutionException("Failed to start \"zig test\"!");
|
||||
}
|
||||
val output = outputOpt.get();
|
||||
if (output.getExitCode() != 0) {
|
||||
throw new ExecutionException("Zig test 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 test binary", e);
|
||||
}
|
||||
|
||||
//Construct new command line
|
||||
val cfg = profileState.configuration();
|
||||
val cli = new GeneralCommandLine().withExePath(executableFile.getAbsolutePath());
|
||||
if (cfg.workingDirectory != null) {
|
||||
cli.withWorkDirectory(cfg.workingDirectory.toString());
|
||||
}
|
||||
cli.withCharset(StandardCharsets.UTF_8);
|
||||
cli.withRedirectErrorStream(true);
|
||||
return cli;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull File getExecutableFile() {
|
||||
return executableFile;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* 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.zig.debugger.win;
|
||||
|
||||
import com.falsepattern.zigbrains.zig.debugger.dap.DAPDebuggerDriverConfiguration;
|
||||
import com.falsepattern.zigbrains.zig.debugger.dap.DAPDriver;
|
||||
import com.falsepattern.zigbrains.zig.debugger.dap.WrappedDebugServer;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.util.system.CpuArch;
|
||||
import com.jetbrains.cidr.ArchitectureType;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.DebuggerCommandException;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.val;
|
||||
import org.eclipse.lsp4j.debug.Capabilities;
|
||||
import org.eclipse.lsp4j.debug.OutputEventArguments;
|
||||
import org.eclipse.lsp4j.debug.services.IDebugProtocolServer;
|
||||
import org.eclipse.lsp4j.jsonrpc.JsonRpcException;
|
||||
import org.eclipse.lsp4j.jsonrpc.MessageConsumer;
|
||||
import org.eclipse.lsp4j.jsonrpc.MessageIssueException;
|
||||
import org.eclipse.lsp4j.jsonrpc.debug.messages.DebugResponseMessage;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Message;
|
||||
import org.eclipse.lsp4j.jsonrpc.services.JsonRequest;
|
||||
import org.eclipse.lsp4j.util.ToStringBuilder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Base64;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.zip.Inflater;
|
||||
|
||||
public class WinDAPDriver extends DAPDriver<
|
||||
IDebugProtocolServer,
|
||||
WrappedDebugServer<IDebugProtocolServer>,
|
||||
WinDAPDriver.WinDAPDebuggerClient
|
||||
> {
|
||||
private final CompletableFuture<HandshakeResponse> handshakeFuture = new CompletableFuture<>();
|
||||
public WinDAPDriver(@NotNull Handler handler, WinDebuggerDriverConfiguration config) throws ExecutionException {
|
||||
super(handler, config);
|
||||
DAPDriver$postConstructor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void DAPDriver$postConstructor$invoke() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MessageConsumer wrapMessageConsumer(MessageConsumer messageConsumer) {
|
||||
return new MessageConsumer() {
|
||||
private boolean verifyHandshake = true;
|
||||
@Override
|
||||
public void consume(Message message) throws MessageIssueException, JsonRpcException {
|
||||
if (verifyHandshake && message instanceof DebugResponseMessage res && res.getMethod().equals("handshake")) {
|
||||
verifyHandshake = false;
|
||||
res.setResponseId(1);
|
||||
}
|
||||
messageConsumer.consume(message);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<IDebugProtocolServer> getServerInterface() {
|
||||
return IDebugProtocolServer.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WrappedDebugServer<IDebugProtocolServer> wrapDebugServer(IDebugProtocolServer remoteProxy) {
|
||||
return new WrappedDebugServer<>(remoteProxy);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WinDAPDebuggerClient createDebuggerClient() {
|
||||
return this.new WinDAPDebuggerClient();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<?> wrapInitialize(CompletableFuture<Capabilities> capabilitiesCompletableFuture) {
|
||||
return capabilitiesCompletableFuture.thenCombine(handshakeFuture, (res, hs) -> res);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
protected class WinDAPDebuggerClient extends DAPDebuggerClient {
|
||||
@Override
|
||||
public void output(OutputEventArguments args) {
|
||||
if ("telemetry".equals(args.getCategory())) {
|
||||
return;
|
||||
}
|
||||
super.output(args);
|
||||
}
|
||||
|
||||
@JsonRequest
|
||||
public CompletableFuture<HandshakeResponse> handshake(HandshakeRequest handshake) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
val hasher = MessageDigest.getInstance("SHA-256");
|
||||
hasher.update(handshake.getValue().getBytes(StandardCharsets.UTF_8));
|
||||
var inflater = new Inflater(true);
|
||||
val coconut = DAPDebuggerClient.class.getResourceAsStream("/coconut.jpg").readAllBytes();
|
||||
inflater.setInput(coconut, coconut.length - 80, 77);
|
||||
inflater.finished();
|
||||
var b = new byte[1];
|
||||
while (inflater.inflate(b) > 0) {
|
||||
hasher.update(b);
|
||||
}
|
||||
return new HandshakeResponse(new String(coconut, coconut.length - 3, 3) + Base64.getEncoder().encodeToString(hasher.digest()));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}).thenApply(handshakeResponse -> {
|
||||
handshakeFuture.complete(handshakeResponse);
|
||||
return handshakeResponse;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getArchitecture() throws ExecutionException {
|
||||
return ArchitectureType.forVmCpuArch(CpuArch.CURRENT).getId();
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public static class HandshakeRequest {
|
||||
@NonNull
|
||||
private String value;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
val b = new ToStringBuilder(this);
|
||||
b.add("value", this.value);
|
||||
return b.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@RequiredArgsConstructor
|
||||
public static class HandshakeResponse {
|
||||
@NonNull
|
||||
private String signature;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
val b = new ToStringBuilder(this);
|
||||
b.add("signature", this.signature);
|
||||
return b.toString();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* 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.zig.debugger.win;
|
||||
|
||||
import com.falsepattern.zigbrains.zig.debugger.win.config.WinDebuggerConfigService;
|
||||
import com.falsepattern.zigbrains.zig.debugger.dap.DAPDebuggerDriverConfiguration;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.jetbrains.cidr.ArchitectureType;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriver;
|
||||
import lombok.val;
|
||||
import org.apache.commons.io.file.PathUtils;
|
||||
import org.eclipse.lsp4j.debug.InitializeRequestArguments;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.Cleaner;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Map;
|
||||
|
||||
public class WinDebuggerDriverConfiguration extends DAPDebuggerDriverConfiguration {
|
||||
private static void extractVSDebugger(Path pathToPluginFile, Path pathToExtracted) throws IOException {
|
||||
if (pathToPluginFile == null) {
|
||||
throw new IllegalArgumentException("Please set the debugger inside Build, Execution, Deployment | Debugger | Zig (Windows)");
|
||||
}
|
||||
if (!Files.isRegularFile(pathToPluginFile) || !pathToPluginFile.getFileName().toString().endsWith(".vsix")) {
|
||||
throw new IllegalArgumentException("Invalid debugger file path! Please check Build, Execution, Deployment | Debugger | Zig (Windows) again! The file MUST be a .vsix file!");
|
||||
}
|
||||
URI uri;
|
||||
try {
|
||||
uri = new URI("jar:" + pathToPluginFile.toAbsolutePath().toUri().toASCIIString());
|
||||
} catch (URISyntaxException e) {
|
||||
throw new IllegalArgumentException("Could not parse plugin file path: " + pathToPluginFile);
|
||||
}
|
||||
try (val fs = FileSystems.newFileSystem(uri, Map.of())) {
|
||||
val basePath = fs.getPath("/extension/debugAdapters/vsdbg/bin");
|
||||
try (val walk = Files.walk(basePath)) {
|
||||
walk.forEach(path -> {
|
||||
if (!Files.isRegularFile(path))
|
||||
return;
|
||||
val relPath = Path.of(basePath.relativize(path).toString());
|
||||
val resPath = pathToExtracted.resolve(relPath);
|
||||
try {
|
||||
Files.createDirectories(resPath.getParent());
|
||||
Files.copy(path, resPath, StandardCopyOption.REPLACE_EXISTING);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull GeneralCommandLine createDriverCommandLine(@NotNull DebuggerDriver debuggerDriver, @NotNull ArchitectureType architectureType) {
|
||||
val pathToPluginFileStr = WinDebuggerConfigService.getInstance().cppToolsPath;
|
||||
if (pathToPluginFileStr == null || pathToPluginFileStr.isBlank()) {
|
||||
throw new IllegalArgumentException("Please set the debugger inside Build, Execution, Deployment | Debugger | Zig (Windows)");
|
||||
}
|
||||
Path pathToPluginFile;
|
||||
try {
|
||||
pathToPluginFile = Path.of(pathToPluginFileStr);
|
||||
} catch (InvalidPathException e) {
|
||||
throw new IllegalArgumentException("Invalid debugger path " + pathToPluginFileStr + "! Make sure it points to the .vsix file!");
|
||||
}
|
||||
Path pathToExtracted;
|
||||
try {
|
||||
val tmpDir = Path.of(System.getProperty("java.io.tmpdir"));
|
||||
int i = 0;
|
||||
do {
|
||||
pathToExtracted = tmpDir.resolve("zb-windbg-" + i++);
|
||||
} while (Files.exists(pathToExtracted.resolve(".lock")) || Files.isRegularFile(pathToExtracted));
|
||||
if (Files.exists(pathToExtracted)) {
|
||||
PathUtils.deleteDirectory(pathToExtracted);
|
||||
}
|
||||
extractVSDebugger(pathToPluginFile, pathToExtracted);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
Path finalPathToExtracted = pathToExtracted;
|
||||
val lockFile = finalPathToExtracted.resolve(".lock");
|
||||
try {
|
||||
Files.createFile(lockFile);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
Cleaner.create().register(debuggerDriver, () -> {
|
||||
try {
|
||||
Files.delete(lockFile);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
val cli = new GeneralCommandLine();
|
||||
cli.setExePath(finalPathToExtracted.resolve("vsdbg.exe").toString());
|
||||
cli.setCharset(StandardCharsets.UTF_8);
|
||||
cli.addParameters("--interpreter=vscode", "--extConfigDir=%USERPROFILE%\\.cppvsdbg\\extensions");
|
||||
cli.setWorkDirectory(finalPathToExtracted.toString());
|
||||
return cli;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getDriverName() {
|
||||
return "WinDAPDriver";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull DebuggerDriver createDriver(DebuggerDriver.@NotNull Handler handler, @NotNull ArchitectureType architectureType)
|
||||
throws ExecutionException {
|
||||
return new WinDAPDriver(handler, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customizeInitializeArguments(InitializeRequestArguments initArgs) {
|
||||
initArgs.setPathFormat("path");
|
||||
initArgs.setAdapterID("cppvsdbg");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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.zig.debugger.win.config;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.components.PersistentStateComponent;
|
||||
import com.intellij.openapi.components.Service;
|
||||
import com.intellij.openapi.components.State;
|
||||
import com.intellij.openapi.components.Storage;
|
||||
import com.intellij.util.xmlb.XmlSerializerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@Service(Service.Level.APP)
|
||||
@State(name = "CPPToolsSettings",
|
||||
storages = @Storage("zigbrains.xml"))
|
||||
public final class WinDebuggerConfigService implements PersistentStateComponent<WinDebuggerConfigService> {
|
||||
public String cppToolsPath = "";
|
||||
|
||||
public static WinDebuggerConfigService getInstance() {
|
||||
return ApplicationManager.getApplication().getService(WinDebuggerConfigService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull WinDebuggerConfigService getState() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadState(@NotNull WinDebuggerConfigService state) {
|
||||
XmlSerializerUtil.copyBean(state, this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* 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.zig.debugger.win.config;
|
||||
|
||||
import com.intellij.openapi.options.Configurable;
|
||||
import com.intellij.openapi.options.ConfigurationException;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.NlsContexts;
|
||||
import com.intellij.ui.JBColor;
|
||||
import com.intellij.util.system.OS;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static com.intellij.ui.dsl.builder.BuilderKt.panel;
|
||||
|
||||
public class WinDebuggerConfigurable implements Configurable {
|
||||
private WinDebuggerSettingsPanel settingsPanel;
|
||||
|
||||
@Override
|
||||
public @NlsContexts.ConfigurableName String getDisplayName() {
|
||||
return "Zig Debugger (Windows)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable JComponent createComponent() {
|
||||
settingsPanel = new WinDebuggerSettingsPanel();
|
||||
return panel((p) -> {
|
||||
if (OS.CURRENT != OS.Windows) {
|
||||
p.row("This menu has no effect on linux/macos/non-windows systems, use the C++ toolchains (see plugin description).", (r) -> null);
|
||||
p.row("For completeness' sake, here is what you would need to configure on Windows:", (r) -> null);
|
||||
p.separator(JBColor.foreground());
|
||||
}
|
||||
settingsPanel.attachPanelTo(p);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isModified() {
|
||||
if (settingsPanel == null)
|
||||
return false;
|
||||
var cppSettings = WinDebuggerConfigService.getInstance();
|
||||
var settingsData = settingsPanel.getData();
|
||||
return !Objects.equals(settingsData.cppToolsPath(), cppSettings.cppToolsPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply() throws ConfigurationException {
|
||||
if (settingsPanel == null)
|
||||
return;
|
||||
var cppSettings = WinDebuggerConfigService.getInstance();
|
||||
var settingsData = settingsPanel.getData();
|
||||
cppSettings.cppToolsPath = settingsData.cppToolsPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
if (settingsPanel == null)
|
||||
return;
|
||||
val cppSettings = WinDebuggerConfigService.getInstance();
|
||||
settingsPanel.setData(new WinDebuggerSettingsPanel.SettingsData(cppSettings.cppToolsPath));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disposeUIResources() {
|
||||
if (settingsPanel == null)
|
||||
return;
|
||||
Disposer.dispose(settingsPanel);
|
||||
settingsPanel = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* 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.zig.debugger.win.config;
|
||||
|
||||
import com.falsepattern.zigbrains.common.util.StringUtil;
|
||||
import com.falsepattern.zigbrains.common.util.TextFieldUtil;
|
||||
import com.falsepattern.zigbrains.project.openapi.MyDisposable;
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.ui.HyperlinkLabel;
|
||||
import com.intellij.ui.dsl.builder.AlignX;
|
||||
import com.intellij.ui.dsl.builder.AlignY;
|
||||
import com.intellij.ui.dsl.builder.Panel;
|
||||
import lombok.Getter;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
import java.util.Optional;
|
||||
|
||||
public class WinDebuggerSettingsPanel implements MyDisposable {
|
||||
@Getter
|
||||
private boolean disposed = false;
|
||||
|
||||
public record SettingsData(@Nullable String cppToolsPath) {}
|
||||
|
||||
@SuppressWarnings("DialogTitleCapitalization")
|
||||
private final TextFieldWithBrowseButton pathToArchive = TextFieldUtil.pathToFileTextField(this,
|
||||
"Path to \"cpptools-win**.vsix\"",
|
||||
() -> {});
|
||||
|
||||
public SettingsData getData() {
|
||||
return new SettingsData(StringUtil.blankToNull(pathToArchive.getText()));
|
||||
}
|
||||
|
||||
public void setData(SettingsData value) {
|
||||
pathToArchive.setText(Optional.ofNullable(value.cppToolsPath()).orElse(""));
|
||||
}
|
||||
|
||||
private static HyperlinkLabel link(String url) {
|
||||
val href = new HyperlinkLabel(url);
|
||||
href.setHyperlinkTarget(url);
|
||||
return href;
|
||||
}
|
||||
|
||||
public void attachPanelTo(Panel panel) {
|
||||
panel.panel(p -> {
|
||||
p.row("Debugging Zig on Windows requires you to manually install the MSVC toolchain.",
|
||||
(r) -> null);
|
||||
|
||||
p.row("To install the MSVC debugger, follow setup 3 under Prerequisites on the following page:",
|
||||
(r) -> null);
|
||||
return null;
|
||||
});
|
||||
panel.panel(p -> {
|
||||
p.row((JLabel) null, (r) -> {
|
||||
r.cell(link("https://code.visualstudio.com/docs/cpp/config-msvc"));
|
||||
return null;
|
||||
});
|
||||
return null;
|
||||
});
|
||||
panel.panel(p -> {
|
||||
p.row("After you've installed MSVC, you also need download the vscode plugin with the debugger adapter.", (r) -> null);
|
||||
p.row("Latest known working version: 1.19.6. Newer versions may or may not work.", (r) -> null);
|
||||
return null;
|
||||
});
|
||||
panel.panel(p -> {
|
||||
p.row("You can download the latest version here:", (r) -> {
|
||||
r.cell(link("https://github.com/microsoft/vscode-cpptools/releases"));
|
||||
return null;
|
||||
});
|
||||
p.row("Put the path to the downloaded file here:", (r) -> {
|
||||
r.cell(pathToArchive).resizableColumn().align(AlignX.FILL).align(AlignY.FILL);
|
||||
return null;
|
||||
});
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
disposed = true;
|
||||
Disposer.dispose(pathToArchive);
|
||||
}
|
||||
}
|
|
@ -19,10 +19,14 @@
|
|||
<resource-bundle>zigbrains.zig.debugger.Bundle</resource-bundle>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<programRunner implementation="com.falsepattern.zigbrains.zig.debugger.run.ZigDebugRunnerRun"
|
||||
<programRunner implementation="com.falsepattern.zigbrains.zig.debugger.runner.run.ZigDebugRunnerRun"
|
||||
id="ZigDebugRunnerRun"/>
|
||||
<programRunner implementation="com.falsepattern.zigbrains.zig.debugger.test.ZigDebugRunnerTest"
|
||||
<programRunner implementation="com.falsepattern.zigbrains.zig.debugger.runner.test.ZigDebugRunnerTest"
|
||||
id="ZigDebugRunnerTest"/>
|
||||
<programRunner implementation="com.falsepattern.zigbrains.zig.debugger.runner.build.ZigDebugRunnerBuild"
|
||||
id="ZigDebugRunnerBuild"/>
|
||||
<programRunner implementation="com.falsepattern.zigbrains.zig.debugger.runner.binary.ZigDebugRunnerBinary"
|
||||
id="ZigDebugRunnerBinary"/>
|
||||
|
||||
<notificationGroup displayType="BALLOON"
|
||||
bundle="zigbrains.zig.debugger.Bundle"
|
||||
|
@ -32,6 +36,9 @@
|
|||
bundle="zigbrains.zig.debugger.Bundle"
|
||||
key="notif-debug-warn"
|
||||
id="ZigBrains.Debugger.Warn"/>
|
||||
<applicationConfigurable parentId="project.propDebugger"
|
||||
instance="com.falsepattern.zigbrains.zig.debugger.win.config.WinDebuggerConfigurable"
|
||||
displayName="Zig (Windows)"/>
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="cidr.debugger">
|
||||
|
|
|
@ -40,7 +40,7 @@ public class ProcessStreamConnectionProvider implements StreamConnectionProvider
|
|||
@Nullable
|
||||
private ProcessBuilder builder;
|
||||
@Nullable
|
||||
private Process process = null;
|
||||
protected Process process = null;
|
||||
private List<String> commands;
|
||||
private String workingDir;
|
||||
|
||||
|
@ -55,7 +55,7 @@ public class ProcessStreamConnectionProvider implements StreamConnectionProvider
|
|||
}
|
||||
|
||||
public void start() throws IOException {
|
||||
if ((workingDir == null || commands == null || commands.isEmpty() || commands.contains(null)) && builder == null) {
|
||||
if ((workingDir == null || commands == null || commands.isEmpty()) && builder == null) {
|
||||
throw new IOException("Unable to start language server: " + this.toString());
|
||||
}
|
||||
ProcessBuilder builder = createProcessBuilder();
|
||||
|
@ -86,6 +86,12 @@ public class ProcessStreamConnectionProvider implements StreamConnectionProvider
|
|||
return process != null ? process.getInputStream() : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public InputStream getErrorStream() {
|
||||
return process != null ? process.getErrorStream() : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public OutputStream getOutputStream() {
|
||||
|
@ -98,6 +104,12 @@ public class ProcessStreamConnectionProvider implements StreamConnectionProvider
|
|||
}
|
||||
}
|
||||
|
||||
public void onExit(Runnable runnable) {
|
||||
if (process != null) {
|
||||
process.onExit().thenRun(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof ProcessStreamConnectionProvider) {
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package com.falsepattern.zigbrains.lsp.common.connection;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
@ -25,6 +26,10 @@ public interface StreamConnectionProvider {
|
|||
|
||||
InputStream getInputStream();
|
||||
|
||||
default @Nullable InputStream getErrorStream() {
|
||||
return null;
|
||||
}
|
||||
|
||||
OutputStream getOutputStream();
|
||||
|
||||
void stop();
|
||||
|
|
|
@ -131,8 +131,7 @@ import static com.falsepattern.zigbrains.lsp.client.languageserver.ServerStatus.
|
|||
* The implementation of a LanguageServerWrapper (specific to a serverDefinition and a project)
|
||||
*/
|
||||
public class LanguageServerWrapper {
|
||||
|
||||
public LanguageServerDefinition serverDefinition;
|
||||
public final LanguageServerDefinition serverDefinition;
|
||||
private final LSPExtensionManager extManager;
|
||||
private final Project project;
|
||||
private final HashSet<Editor> toConnect = new HashSet<>();
|
||||
|
|
|
@ -17,57 +17,64 @@
|
|||
package com.falsepattern.zigbrains.project.console;
|
||||
|
||||
import com.falsepattern.zigbrains.common.util.FileUtil;
|
||||
import com.falsepattern.zigbrains.project.openapi.module.ZigModuleType;
|
||||
import com.intellij.execution.filters.Filter;
|
||||
import com.intellij.execution.filters.OpenFileHyperlinkInfo;
|
||||
import com.intellij.ide.impl.ProjectUtil;
|
||||
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
|
||||
import com.intellij.openapi.module.ModuleUtil;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import kotlin.Pair;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class ZigSourceFileFilter implements Filter {
|
||||
private final Project project;
|
||||
private final Pattern LEN_REGEX = Pattern.compile(":(\\d+):(\\d+)");
|
||||
|
||||
private Pair<Path, Integer> findLongestParsablePathFromOffset(String line, int end, String projectPath) {
|
||||
int longestStart = -1;
|
||||
Path longest = null;
|
||||
for (int i = end - 1; i >= 0; i--) {
|
||||
try {
|
||||
val pathStr = line.substring(i, end);
|
||||
var path = Path.of(pathStr);
|
||||
if (!Files.exists(path) || !Files.isRegularFile(path)) {
|
||||
path = Path.of(projectPath, pathStr);
|
||||
if (!Files.exists(path) || !Files.isRegularFile(path))
|
||||
continue;
|
||||
}
|
||||
longest = path;
|
||||
longestStart = i;
|
||||
} catch (InvalidPathException ignored){}
|
||||
}
|
||||
return new Pair<>(longest, longestStart);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Result applyFilter(@NotNull String line, int entireLength) {
|
||||
val lineStart = entireLength - line.length();
|
||||
int splitA, splitB, splitC;
|
||||
splitA = line.indexOf(':');
|
||||
if (splitA < 0)
|
||||
return null;
|
||||
splitB = line.indexOf(':', splitA + 1);
|
||||
if (splitB < 0)
|
||||
return null;
|
||||
splitC = line.indexOf(':', splitB + 1);
|
||||
if (splitC < 0)
|
||||
return null;
|
||||
final int lineNumber, lineOffset;
|
||||
try {
|
||||
lineNumber = Math.max(Integer.parseInt(line, splitA + 1, splitB, 10) - 1, 0);
|
||||
lineOffset = Math.max(Integer.parseInt(line, splitB + 1, splitC, 10) - 1, 0);
|
||||
} catch (NumberFormatException ignored) {
|
||||
return null;
|
||||
}
|
||||
val pathStr = line.substring(0, splitA);
|
||||
var path = Path.of(pathStr);
|
||||
if (!Files.exists(path) || !Files.isRegularFile(path)) {
|
||||
val projectPath = project.getBasePath();
|
||||
if (projectPath == null)
|
||||
return null;
|
||||
path = Path.of(projectPath, pathStr);
|
||||
if (!Files.exists(path) || !Files.isRegularFile(path))
|
||||
val projectPath = project.getBasePath();
|
||||
val results = new ArrayList<ResultItem>();
|
||||
val matcher = LEN_REGEX.matcher(line);
|
||||
while (matcher.find()) {
|
||||
val end = matcher.start();
|
||||
val pair = findLongestParsablePathFromOffset(line, end, projectPath);
|
||||
val path = pair.getFirst();
|
||||
if (path == null)
|
||||
return null;
|
||||
val lineNumber = Math.max(Integer.parseInt(matcher.group(1)) - 1, 0);
|
||||
val lineOffset = Math.max(Integer.parseInt(matcher.group(2)) - 1, 0);
|
||||
val file = FileUtil.virtualFileFromURI(path.toUri());
|
||||
results.add(new ResultItem(lineStart + pair.getSecond(), lineStart + matcher.end(), new OpenFileHyperlinkInfo(project, file, lineNumber, lineOffset)));
|
||||
|
||||
}
|
||||
val file = FileUtil.virtualFileFromURI(path.toUri());
|
||||
return new Result(lineStart, lineStart + splitC, new OpenFileHyperlinkInfo(project, file, lineNumber, lineOffset));
|
||||
return new Result(results);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@ import com.intellij.psi.PsiElement;
|
|||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public abstract class ConfigProducerBase<T extends ZigExecConfigBase<T>> extends LazyRunConfigurationProducer<T> {
|
||||
@NotNull
|
||||
@Override
|
||||
|
@ -47,7 +49,7 @@ public abstract class ConfigProducerBase<T extends ZigExecConfigBase<T>> extends
|
|||
return false;
|
||||
}
|
||||
var theFile = psiFile.getVirtualFile();
|
||||
var filePath = theFile.getPath();
|
||||
var filePath = theFile.toNioPath();
|
||||
return setupConfigurationFromContext(configuration, element, filePath, theFile);
|
||||
}
|
||||
|
||||
|
@ -65,7 +67,7 @@ public abstract class ConfigProducerBase<T extends ZigExecConfigBase<T>> extends
|
|||
return false;
|
||||
}
|
||||
val vFile = file.getVirtualFile();
|
||||
val filePath = vFile.getPath();
|
||||
val filePath = vFile.toNioPath();
|
||||
return isConfigurationFromContext(configuration, filePath, vFile, element);
|
||||
}
|
||||
|
||||
|
@ -95,6 +97,6 @@ public abstract class ConfigProducerBase<T extends ZigExecConfigBase<T>> extends
|
|||
}
|
||||
*/
|
||||
|
||||
protected abstract boolean setupConfigurationFromContext(@NotNull T configuration, PsiElement element, String filePath, VirtualFile theFile);
|
||||
protected abstract boolean isConfigurationFromContext(@NotNull T configuration, String filePath, VirtualFile vFile, PsiElement element);
|
||||
protected abstract boolean setupConfigurationFromContext(@NotNull T configuration, PsiElement element, Path filePath, VirtualFile theFile);
|
||||
protected abstract boolean isConfigurationFromContext(@NotNull T configuration, Path filePath, VirtualFile vFile, PsiElement element);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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.project.execution.base;
|
||||
|
||||
public enum OptimizationLevel {
|
||||
Debug,
|
||||
ReleaseFast,
|
||||
ReleaseSafe,
|
||||
ReleaseSmall
|
||||
}
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package com.falsepattern.zigbrains.project.execution.base;
|
||||
|
||||
import com.falsepattern.zigbrains.project.execution.ZigCapturingProcessHandler;
|
||||
import com.falsepattern.zigbrains.project.runconfig.ZigProcessHandler;
|
||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||
import com.falsepattern.zigbrains.project.util.ProjectUtil;
|
||||
|
@ -44,18 +43,20 @@ public abstract class ProfileStateBase<T extends ZigExecConfigBase<T>> extends C
|
|||
|
||||
@Override
|
||||
protected @NotNull ProcessHandler startProcess() throws ExecutionException {
|
||||
return new ZigProcessHandler(getCommandLine(ProjectUtil.getToolchain(getEnvironment().getProject())));
|
||||
return new ZigProcessHandler(getCommandLine(ProjectUtil.getToolchain(getEnvironment().getProject()), false));
|
||||
}
|
||||
|
||||
public GeneralCommandLine getCommandLine(AbstractZigToolchain toolchain) {
|
||||
val workingDirectory = configuration.workingDirectory;
|
||||
public GeneralCommandLine getCommandLine(AbstractZigToolchain toolchain, boolean debug) throws ExecutionException {
|
||||
val workingDirectory = configuration.getWorkingDirectory();
|
||||
val zigExecutablePath = toolchain.pathToExecutable("zig");
|
||||
|
||||
return new GeneralCommandLine().withExePath(zigExecutablePath.toString())
|
||||
.withWorkDirectory(workingDirectory.toString())
|
||||
.withCharset(StandardCharsets.UTF_8)
|
||||
.withRedirectErrorStream(true)
|
||||
.withParameters(configuration.buildCommandLineArgs());
|
||||
val cli = new GeneralCommandLine();
|
||||
cli.setExePath(zigExecutablePath.toString());
|
||||
workingDirectory.getPath().ifPresent(x -> cli.setWorkDirectory(x.toFile()));
|
||||
cli.setCharset(StandardCharsets.UTF_8);
|
||||
cli.setRedirectErrorStream(true);
|
||||
cli.addParameters(debug ? configuration.buildDebugCommandLineArgs() : configuration.buildCommandLineArgs());
|
||||
return cli;
|
||||
}
|
||||
|
||||
public T configuration() {
|
||||
|
@ -65,7 +66,7 @@ public abstract class ProfileStateBase<T extends ZigExecConfigBase<T>> extends C
|
|||
public DefaultExecutionResult executeCommandLine(GeneralCommandLine commandLine, ExecutionEnvironment environment)
|
||||
throws ExecutionException {
|
||||
val handler = startProcess(commandLine);
|
||||
val console = new BuildTextConsoleView(environment.getProject(), true, Collections.emptyList());
|
||||
val console = new BuildTextConsoleView(environment.getProject(), false, Collections.emptyList());
|
||||
console.attachToProcess(handler);
|
||||
return new DefaultExecutionResult(console, handler);
|
||||
}
|
||||
|
|
|
@ -18,137 +18,463 @@ package com.falsepattern.zigbrains.project.execution.base;
|
|||
|
||||
import com.falsepattern.zigbrains.project.ui.WorkingDirectoryComponent;
|
||||
import com.falsepattern.zigbrains.project.ui.ZigFilePathPanel;
|
||||
import com.falsepattern.zigbrains.project.util.CLIUtil;
|
||||
import com.falsepattern.zigbrains.project.util.ElementUtil;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.options.ConfigurationException;
|
||||
import com.intellij.openapi.options.SettingsEditor;
|
||||
import com.intellij.openapi.ui.LabeledComponent;
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||
import com.intellij.openapi.ui.ComboBox;
|
||||
import com.intellij.ui.components.JBCheckBox;
|
||||
import com.intellij.ui.components.JBTextField;
|
||||
import com.intellij.ui.dsl.builder.AlignX;
|
||||
import com.intellij.ui.dsl.builder.AlignY;
|
||||
import com.intellij.ui.dsl.builder.Panel;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.val;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
import java.io.Serializable;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import static com.intellij.ui.dsl.builder.BuilderKt.panel;
|
||||
|
||||
public class ZigConfigEditor<T extends ZigExecConfigBase<T>> extends SettingsEditor<T> {
|
||||
private final List<ZigConfigModule<T>> modules;
|
||||
private final ZigExecConfigBase<T> state;
|
||||
private final List<ZigConfigurable.ZigConfigModule<?>> configModules = new ArrayList<>();
|
||||
|
||||
public ZigConfigEditor(List<ZigConfigModule<T>> modules) {
|
||||
this.modules = new ArrayList<>(modules);
|
||||
public ZigConfigEditor(ZigExecConfigBase<T> state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyEditorTo(@NotNull T s) throws ConfigurationException {
|
||||
for (val module: modules) {
|
||||
module.applyTo(s);
|
||||
try {
|
||||
outer:
|
||||
for (val cfg : s.getConfigurables()) {
|
||||
for (val module : configModules) {
|
||||
if (module.tryApply(cfg))
|
||||
continue outer;
|
||||
}
|
||||
System.err.println("EEE");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetEditorFrom(@NotNull T s) {
|
||||
for (val module: modules) {
|
||||
module.resetFrom(s);
|
||||
outer:
|
||||
for (val cfg: s.getConfigurables()) {
|
||||
for (val module: configModules) {
|
||||
if (module.tryReset(cfg))
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final @NotNull JComponent createEditor() {
|
||||
configModules.clear();
|
||||
configModules.addAll(state.getConfigurables().stream().map(ZigConfigurable::createEditor).toList());
|
||||
return panel((p) -> {
|
||||
for (val module: modules) {
|
||||
for (val module: configModules) {
|
||||
module.construct(p);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public interface ZigConfigModule<T> {
|
||||
void applyTo(@NotNull T s) throws ConfigurationException;
|
||||
void resetFrom(@NotNull T s);
|
||||
void construct(Panel p);
|
||||
@Override
|
||||
protected void disposeEditor() {
|
||||
for (val module: configModules) {
|
||||
module.dispose();
|
||||
}
|
||||
configModules.clear();
|
||||
}
|
||||
|
||||
public static class WorkingDirectoryModule<T extends ZigExecConfigBase<T>> implements ZigConfigModule<T> {
|
||||
protected final LabeledComponent<TextFieldWithBrowseButton> workingDirectoryComponent =
|
||||
new WorkingDirectoryComponent();
|
||||
public interface ZigConfigurable<T extends ZigConfigurable<T>> extends Serializable, Cloneable {
|
||||
void readExternal(@NotNull Element element);
|
||||
void writeExternal(@NotNull Element element);
|
||||
ZigConfigModule<T> createEditor();
|
||||
T clone();
|
||||
|
||||
@Override
|
||||
public void applyTo(@NotNull T s) {
|
||||
s.workingDirectory = Paths.get(workingDirectoryComponent.getComponent().getText());
|
||||
}
|
||||
interface ZigConfigModule<T extends ZigConfigurable<T>> extends Disposable {
|
||||
@Nullable T tryMatch(ZigConfigurable<?> cfg);
|
||||
void apply(T configurable) throws ConfigurationException;
|
||||
void reset(T configurable);
|
||||
default boolean tryApply(ZigConfigurable<?> cfg) throws ConfigurationException {
|
||||
val x = tryMatch(cfg);
|
||||
if (x != null) {
|
||||
apply(x);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetFrom(@NotNull T s) {
|
||||
workingDirectoryComponent.getComponent().setText(Objects.requireNonNullElse(s.workingDirectory, "").toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void construct(Panel p) {
|
||||
p.row(workingDirectoryComponent.getLabel(), (r) -> {
|
||||
r.cell(workingDirectoryComponent).resizableColumn().align(AlignX.FILL).align(AlignY.FILL);
|
||||
return null;
|
||||
});
|
||||
default boolean tryReset(ZigConfigurable<?> cfg) {
|
||||
val x = tryMatch(cfg);
|
||||
if (x != null) {
|
||||
reset(x);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void construct(Panel p);
|
||||
}
|
||||
}
|
||||
|
||||
public static class FilePathModule<T extends ZigExecConfigBase<T> & FilePathModule.Carrier> implements ZigConfigModule<T> {
|
||||
private final ZigFilePathPanel filePathPanel = new ZigFilePathPanel();
|
||||
public static abstract class PathConfigurable<T extends PathConfigurable<T>> implements ZigConfigurable<T> {
|
||||
private @Nullable Path path = null;
|
||||
|
||||
@Override
|
||||
public void applyTo(@NotNull T s) {
|
||||
s.setFilePath(filePathPanel.getText());
|
||||
public Optional<Path> getPath() {
|
||||
return Optional.ofNullable(path);
|
||||
}
|
||||
|
||||
public @NotNull Path getPathOrThrow() {
|
||||
return getPath().orElseThrow(() -> new IllegalArgumentException("Empty file path!"));
|
||||
}
|
||||
|
||||
public void setPath(@Nullable Path path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetFrom(@NotNull T s) {
|
||||
filePathPanel.setText(Objects.requireNonNullElse(s.getFilePath(), ""));
|
||||
public void readExternal(@NotNull Element element) {
|
||||
try {
|
||||
ElementUtil.readString(element, getSerializedName()).map(Paths::get).ifPresent(x -> path = x);
|
||||
} catch (InvalidPathException ignored){}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void construct(Panel p) {
|
||||
p.row("Target file", (r) -> {
|
||||
r.cell(filePathPanel).resizableColumn().align(AlignX.FILL).align(AlignY.FILL);
|
||||
return null;
|
||||
});
|
||||
public void writeExternal(@NotNull Element element) {
|
||||
ElementUtil.writeString(element, getSerializedName(), path == null ? null : path.toString());
|
||||
}
|
||||
|
||||
public interface Carrier {
|
||||
void setFilePath(String path);
|
||||
String getFilePath();
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public T clone() {
|
||||
return (T) super.clone();
|
||||
}
|
||||
|
||||
protected abstract String getSerializedName();
|
||||
|
||||
public abstract static class PathConfigModule<T extends PathConfigurable<T>> implements ZigConfigModule<T> {
|
||||
@Override
|
||||
public void apply(T s) throws ConfigurationException {
|
||||
try {
|
||||
s.setPath(Paths.get(getString()));
|
||||
} catch (InvalidPathException e) {
|
||||
throw new ConfigurationException(e.getMessage(), e, "Invalid Path");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(T s) {
|
||||
setString(s.getPath().map(Path::toString).orElse(""));
|
||||
}
|
||||
|
||||
protected abstract String getString();
|
||||
protected abstract void setString(String str);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ColoredModule<T extends ZigExecConfigBase<T> & ColoredModule.Carrier> implements ZigConfigModule<T> {
|
||||
private final JBCheckBox checkBox = new JBCheckBox();
|
||||
@Getter(AccessLevel.PROTECTED)
|
||||
@RequiredArgsConstructor
|
||||
public static class WorkDirectoryConfigurable extends PathConfigurable<WorkDirectoryConfigurable> {
|
||||
private transient final String serializedName;
|
||||
|
||||
@Override
|
||||
public void applyTo(@NotNull T s) throws ConfigurationException {
|
||||
s.setColored(checkBox.isSelected());
|
||||
public WorkDirectoryConfigModule createEditor() {
|
||||
return new WorkDirectoryConfigModule(serializedName);
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public static class WorkDirectoryConfigModule extends PathConfigModule<WorkDirectoryConfigurable> {
|
||||
private final String serializedName;
|
||||
@Override
|
||||
public @Nullable WorkDirectoryConfigurable tryMatch(ZigConfigurable<?> cfg) {
|
||||
return cfg instanceof WorkDirectoryConfigurable cfg$ && cfg$.serializedName.equals(serializedName) ? cfg$ : null;
|
||||
}
|
||||
@Override
|
||||
protected String getString() {
|
||||
return workingDirectoryComponent.getComponent().getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setString(String str) {
|
||||
workingDirectoryComponent.getComponent().setText(str);
|
||||
}
|
||||
|
||||
private final WorkingDirectoryComponent workingDirectoryComponent = new WorkingDirectoryComponent(this);
|
||||
|
||||
@Override
|
||||
public void construct(Panel p) {
|
||||
p.row(workingDirectoryComponent.getLabel(), (r) -> {
|
||||
r.cell(workingDirectoryComponent).resizableColumn().align(AlignX.FILL).align(AlignY.FILL);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
workingDirectoryComponent.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public static class FilePathConfigurable extends PathConfigurable<FilePathConfigurable> {
|
||||
@Getter(AccessLevel.PROTECTED)
|
||||
private transient final String serializedName;
|
||||
private transient final String guiLabel;
|
||||
|
||||
@Override
|
||||
public FilePathConfigModule createEditor() {
|
||||
return new FilePathConfigModule(serializedName, guiLabel);
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public static class FilePathConfigModule extends PathConfigModule<FilePathConfigurable> {
|
||||
private final String serializedName;
|
||||
private final String label;
|
||||
private final ZigFilePathPanel filePathPanel = new ZigFilePathPanel();
|
||||
|
||||
@Override
|
||||
public @Nullable FilePathConfigurable tryMatch(ZigConfigurable<?> cfg) {
|
||||
return cfg instanceof FilePathConfigurable cfg$ && cfg$.serializedName.equals(serializedName) ? cfg$ : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getString() {
|
||||
return filePathPanel.getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setString(String str) {
|
||||
filePathPanel.setText(str);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void construct(Panel p) {
|
||||
p.row(label, (r) -> {
|
||||
r.cell(filePathPanel).resizableColumn().align(AlignX.FILL).align(AlignY.FILL);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public static class ColoredConfigurable implements ZigConfigurable<ColoredConfigurable> {
|
||||
private transient final String serializedName;
|
||||
public boolean colored = true;
|
||||
|
||||
@Override
|
||||
public void readExternal(@NotNull Element element) {
|
||||
ElementUtil.readBoolean(element, serializedName).ifPresent(x -> colored = x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetFrom(@NotNull T s) {
|
||||
checkBox.setSelected(s.isColored());
|
||||
public void writeExternal(@NotNull Element element) {
|
||||
ElementUtil.writeBoolean(element, serializedName, colored);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void construct(Panel p) {
|
||||
p.row("Colored terminal", (r) -> {
|
||||
r.cell(checkBox);
|
||||
return null;
|
||||
public ColoredConfigModule createEditor() {
|
||||
return new ColoredConfigModule(serializedName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public ColoredConfigurable clone() {
|
||||
return (ColoredConfigurable) super.clone();
|
||||
}
|
||||
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public static class ColoredConfigModule implements ZigConfigModule<ColoredConfigurable> {
|
||||
private final String serializedName;
|
||||
private final JBCheckBox checkBox = new JBCheckBox();
|
||||
|
||||
@Override
|
||||
public @Nullable ColoredConfigurable tryMatch(ZigConfigurable<?> cfg) {
|
||||
return cfg instanceof ColoredConfigurable cfg$ && cfg$.serializedName.equals(serializedName) ? cfg$ : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(ColoredConfigurable s) throws ConfigurationException {
|
||||
s.colored = checkBox.isSelected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(ColoredConfigurable s) {
|
||||
checkBox.setSelected(s.colored);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void construct(Panel p) {
|
||||
p.row("Colored terminal", (r) -> {
|
||||
r.cell(checkBox);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public static class OptimizationConfigurable implements ZigConfigurable<OptimizationConfigurable> {
|
||||
private transient final String serializedName;
|
||||
public OptimizationLevel level = OptimizationLevel.Debug;
|
||||
public boolean forced = false;
|
||||
|
||||
@Override
|
||||
public void readExternal(@NotNull Element element) {
|
||||
ElementUtil.readChild(element, serializedName).ifPresent(child -> {
|
||||
ElementUtil.readEnum(child, "level", OptimizationLevel.class).ifPresent(x -> level = x);
|
||||
ElementUtil.readBoolean(child,"forced").ifPresent(x -> forced = x);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeExternal(@NotNull Element element) {
|
||||
val child = ElementUtil.writeChild(element, serializedName);
|
||||
ElementUtil.writeEnum(child, "level", level);
|
||||
ElementUtil.writeBoolean(child, "forced", forced);
|
||||
}
|
||||
|
||||
public interface Carrier {
|
||||
void setColored(boolean color);
|
||||
boolean isColored();
|
||||
@Override
|
||||
public OptimizationConfigModule createEditor() {
|
||||
return new OptimizationConfigModule(serializedName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public OptimizationConfigurable clone() {
|
||||
return (OptimizationConfigurable) super.clone();
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public static class OptimizationConfigModule implements ZigConfigModule<OptimizationConfigurable> {
|
||||
private final String serializedName;
|
||||
private final ComboBox<OptimizationLevel> levels = new ComboBox<>(OptimizationLevel.values());
|
||||
private final JBCheckBox forced = new JBCheckBox("Force even in debug runs");
|
||||
|
||||
@Override
|
||||
public @Nullable OptimizationConfigurable tryMatch(ZigConfigurable<?> cfg) {
|
||||
return cfg instanceof OptimizationConfigurable cfg$ && cfg$.serializedName.equals(serializedName) ? cfg$ : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(OptimizationConfigurable s) throws ConfigurationException {
|
||||
s.level = levels.getItem();
|
||||
s.forced = forced.isSelected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(OptimizationConfigurable s) {
|
||||
levels.setItem(s.level);
|
||||
forced.setSelected(s.forced);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void construct(Panel p) {
|
||||
p.row("Optimization level", (r) -> {
|
||||
r.cell(levels);
|
||||
r.cell(forced);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public static class ArgsConfigurable implements ZigConfigurable<ArgsConfigurable> {
|
||||
private transient final String serializedName;
|
||||
private transient final String guiName;
|
||||
public String[] args = new String[0];
|
||||
|
||||
@Override
|
||||
public void readExternal(@NotNull Element element) {
|
||||
ElementUtil.readStrings(element, serializedName).ifPresent(x -> args = x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeExternal(@NotNull Element element) {
|
||||
ElementUtil.writeStrings(element, serializedName, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArgsConfigModule createEditor() {
|
||||
return new ArgsConfigModule(serializedName, guiName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public ArgsConfigurable clone() {
|
||||
return (ArgsConfigurable) super.clone();
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public static class ArgsConfigModule implements ZigConfigModule<ArgsConfigurable> {
|
||||
private final String serializedName;
|
||||
private final String guiName;
|
||||
private final JBTextField argsField = new JBTextField();
|
||||
|
||||
@Override
|
||||
public @Nullable ArgsConfigurable tryMatch(ZigConfigurable<?> cfg) {
|
||||
return cfg instanceof ArgsConfigurable cfg$ && cfg$.serializedName.equals(serializedName) ? cfg$ : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(ArgsConfigurable s) throws ConfigurationException {
|
||||
s.args = CLIUtil.translateCommandline(argsField.getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(ArgsConfigurable s) {
|
||||
argsField.setText(String.join(" ", s.args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void construct(Panel p) {
|
||||
p.row(guiName, (r) -> {
|
||||
r.cell(argsField).resizableColumn().align(AlignX.FILL).align(AlignY.FILL);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,17 +17,18 @@
|
|||
package com.falsepattern.zigbrains.project.execution.base;
|
||||
|
||||
import com.falsepattern.zigbrains.project.util.ElementUtil;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.Executor;
|
||||
import com.intellij.execution.configurations.ConfigurationFactory;
|
||||
import com.intellij.execution.configurations.LocatableConfigurationBase;
|
||||
import com.intellij.execution.configurations.RunConfiguration;
|
||||
import com.intellij.execution.configurations.RunConfigurationBase;
|
||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||
import com.intellij.openapi.options.SettingsEditor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.InvalidDataException;
|
||||
import com.intellij.openapi.util.NlsActions;
|
||||
import lombok.Getter;
|
||||
import lombok.val;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
@ -35,44 +36,50 @@ import org.jetbrains.annotations.Nullable;
|
|||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Getter
|
||||
public abstract class ZigExecConfigBase<T extends ZigExecConfigBase<T>> extends LocatableConfigurationBase<ProfileStateBase<T>> {
|
||||
public @Nullable Path workingDirectory;
|
||||
private ZigConfigEditor.WorkDirectoryConfigurable workingDirectory = new ZigConfigEditor.WorkDirectoryConfigurable("workingDirectory");
|
||||
public ZigExecConfigBase(@NotNull Project project, @NotNull ConfigurationFactory factory, @Nullable String name) {
|
||||
super(project, factory, name);
|
||||
workingDirectory = project.isDefault() ? null : Optional.ofNullable(project.getBasePath())
|
||||
.map(Path::of)
|
||||
.orElse(null);
|
||||
workingDirectory.setPath(project.isDefault() ? null : Optional.ofNullable(project.getBasePath())
|
||||
.map(Path::of)
|
||||
.orElse(null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull SettingsEditor<? extends RunConfiguration> getConfigurationEditor() {
|
||||
return new ZigConfigEditor<>(getEditorConfigModules());
|
||||
public @NotNull ZigConfigEditor<T> getConfigurationEditor() {
|
||||
return new ZigConfigEditor<>(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void readExternal(@NotNull Element element) throws InvalidDataException {
|
||||
super.readExternal(element);
|
||||
ElementUtil.readString(element, "workingDirectory").ifPresent(dir -> {
|
||||
try {
|
||||
workingDirectory = Path.of(dir);
|
||||
} catch (InvalidPathException ignored) {}
|
||||
});
|
||||
getConfigurables().forEach(cfg -> cfg.readExternal(element));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeExternal(@NotNull Element element) {
|
||||
super.writeExternal(element);
|
||||
if (workingDirectory != null)
|
||||
ElementUtil.writeString(element, "workingDirectory", workingDirectory.toString());
|
||||
getConfigurables().forEach(cfg -> cfg.writeExternal(element));
|
||||
}
|
||||
|
||||
public abstract String[] buildCommandLineArgs();
|
||||
|
||||
public String[] buildDebugCommandLineArgs() {
|
||||
return buildCommandLineArgs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T clone() {
|
||||
val myClone = (ZigExecConfigBase<?>) super.clone();
|
||||
myClone.workingDirectory = workingDirectory.clone();
|
||||
return (T) myClone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract @Nullable @NlsActions.ActionText String suggestedName();
|
||||
|
||||
|
@ -80,7 +87,7 @@ public abstract class ZigExecConfigBase<T extends ZigExecConfigBase<T>> extends
|
|||
public abstract @Nullable ProfileStateBase<T> getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment)
|
||||
throws ExecutionException;
|
||||
|
||||
public @NotNull List<ZigConfigEditor.@NotNull ZigConfigModule<T>> getEditorConfigModules() {
|
||||
return new ArrayList<>(List.of(new ZigConfigEditor.WorkingDirectoryModule<>()));
|
||||
public @NotNull List<ZigConfigEditor.@NotNull ZigConfigurable<?>> getConfigurables() {
|
||||
return List.of(workingDirectory);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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.project.execution.binary;
|
||||
|
||||
import com.falsepattern.zigbrains.zig.Icons;
|
||||
import com.intellij.execution.configurations.ConfigurationFactory;
|
||||
import com.intellij.execution.configurations.ConfigurationTypeBase;
|
||||
import com.intellij.execution.configurations.RunConfiguration;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ConfigTypeBinary extends ConfigurationTypeBase {
|
||||
public static final String IDENTIFIER = "ZIGBRAINS_BINARY";
|
||||
public ConfigTypeBinary() {
|
||||
super(IDENTIFIER, "Zig-compiled native executable", "Binary executable compiled from zig code", Icons.ZIG);
|
||||
addFactory(new ConfigFactoryBinary());
|
||||
}
|
||||
|
||||
public class ConfigFactoryBinary extends ConfigurationFactory {
|
||||
public ConfigFactoryBinary() {
|
||||
super(ConfigTypeBinary.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull RunConfiguration createTemplateConfiguration(@NotNull Project project) {
|
||||
return new ZigExecConfigBinary(project, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull @NonNls String getId() {
|
||||
return IDENTIFIER;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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.project.execution.binary;
|
||||
|
||||
import com.falsepattern.zigbrains.project.execution.base.ProfileStateBase;
|
||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||
import lombok.val;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class ProfileStateBinary extends ProfileStateBase<ZigExecConfigBinary> {
|
||||
public ProfileStateBinary(ExecutionEnvironment environment, ZigExecConfigBinary configuration) {
|
||||
super(environment, configuration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeneralCommandLine getCommandLine(AbstractZigToolchain toolchain, boolean debug) throws ExecutionException {
|
||||
val cli = new GeneralCommandLine();
|
||||
val cfg = configuration();
|
||||
cfg.getWorkingDirectory().getPath().ifPresent(dir -> cli.setWorkDirectory(dir.toFile()));
|
||||
cli.setExePath(cfg.getExePath().getPath().orElseThrow(() -> new ExecutionException("Missing executable path")).toString());
|
||||
cli.setCharset(StandardCharsets.UTF_8);
|
||||
cli.setRedirectErrorStream(true);
|
||||
cli.addParameters(cfg.getArgs().args);
|
||||
return cli;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* 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.project.execution.binary;
|
||||
|
||||
import com.falsepattern.zigbrains.common.util.CollectionUtil;
|
||||
import com.falsepattern.zigbrains.project.execution.base.ZigConfigEditor;
|
||||
import com.falsepattern.zigbrains.project.execution.base.ZigExecConfigBase;
|
||||
import com.intellij.execution.Executor;
|
||||
import com.intellij.execution.configurations.ConfigurationFactory;
|
||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
public class ZigExecConfigBinary extends ZigExecConfigBase<ZigExecConfigBinary> {
|
||||
private ZigConfigEditor.FilePathConfigurable exePath = new ZigConfigEditor.FilePathConfigurable("exePath", "Executable program path (not the zig compiler)");
|
||||
private ZigConfigEditor.ArgsConfigurable args = new ZigConfigEditor.ArgsConfigurable("args", "Command line arguments");
|
||||
|
||||
public ZigExecConfigBinary(@NotNull Project project, @NotNull ConfigurationFactory factory) {
|
||||
super(project, factory, "Zig-compiled native executable");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] buildCommandLineArgs() {
|
||||
return args.args;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String suggestedName() {
|
||||
return "Executable";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ZigExecConfigBinary clone() {
|
||||
val clone = super.clone();
|
||||
clone.exePath = exePath.clone();
|
||||
clone.args = args.clone();
|
||||
return clone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<ZigConfigEditor.ZigConfigurable<?>> getConfigurables() {
|
||||
return CollectionUtil.concat(super.getConfigurables(), exePath, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ProfileStateBinary getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) {
|
||||
return new ProfileStateBinary(environment, this);
|
||||
}
|
||||
}
|
|
@ -20,8 +20,11 @@ import com.falsepattern.zigbrains.project.execution.base.ConfigProducerBase;
|
|||
import com.intellij.execution.configurations.ConfigurationFactory;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class ConfigProducerBuild extends ConfigProducerBase<ZigExecConfigBuild> {
|
||||
@Override
|
||||
public @NotNull ConfigurationFactory getConfigurationFactory() {
|
||||
|
@ -29,7 +32,7 @@ public class ConfigProducerBuild extends ConfigProducerBase<ZigExecConfigBuild>
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean setupConfigurationFromContext(@NotNull ZigExecConfigBuild configuration, PsiElement element, String filePath, VirtualFile theFile) {
|
||||
protected boolean setupConfigurationFromContext(@NotNull ZigExecConfigBuild configuration, PsiElement element, Path filePath, VirtualFile theFile) {
|
||||
if (ZigLineMarkerBuild.UTILITY_INSTANCE.elementMatches(element)) {
|
||||
configuration.setName("Build");
|
||||
return true;
|
||||
|
@ -38,7 +41,11 @@ public class ConfigProducerBuild extends ConfigProducerBase<ZigExecConfigBuild>
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean isConfigurationFromContext(@NotNull ZigExecConfigBuild configuration, String filePath, VirtualFile vFile, PsiElement element) {
|
||||
return true;
|
||||
protected boolean isConfigurationFromContext(@NotNull ZigExecConfigBuild configuration, Path filePath, VirtualFile vFile, PsiElement element) {
|
||||
val p = configuration.getWorkingDirectory().getPath();
|
||||
if (p.isEmpty())
|
||||
return false;
|
||||
val path = p.get();
|
||||
return filePath.getParent().equals(path);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,48 +16,34 @@
|
|||
|
||||
package com.falsepattern.zigbrains.project.execution.build;
|
||||
|
||||
import com.falsepattern.zigbrains.common.util.CollectionUtil;
|
||||
import com.falsepattern.zigbrains.project.execution.base.ZigConfigEditor;
|
||||
import com.falsepattern.zigbrains.project.execution.base.ZigExecConfigBase;
|
||||
import com.falsepattern.zigbrains.project.util.ElementUtil;
|
||||
import com.intellij.execution.Executor;
|
||||
import com.intellij.execution.configurations.ConfigurationFactory;
|
||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||
import com.intellij.openapi.options.ConfigurationException;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.InvalidDataException;
|
||||
import com.intellij.ui.components.JBTextField;
|
||||
import com.intellij.ui.dsl.builder.AlignX;
|
||||
import com.intellij.ui.dsl.builder.AlignY;
|
||||
import com.intellij.ui.dsl.builder.Panel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.val;
|
||||
import org.apache.groovy.util.Arrays;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class ZigExecConfigBuild extends ZigExecConfigBase<ZigExecConfigBuild> implements
|
||||
ZigConfigEditor.ColoredModule.Carrier {
|
||||
public String extraArguments = "";
|
||||
public boolean colored = true;
|
||||
public class ZigExecConfigBuild extends ZigExecConfigBase<ZigExecConfigBuild> {
|
||||
private ZigConfigEditor.ArgsConfigurable extraArgs = new ZigConfigEditor.ArgsConfigurable("extraArgs", "Extra command line arguments");
|
||||
private ZigConfigEditor.ColoredConfigurable colored = new ZigConfigEditor.ColoredConfigurable("colored");
|
||||
private ZigConfigEditor.FilePathConfigurable exePath = new ZigConfigEditor.FilePathConfigurable("exePath", "Output executable created by the build (for debugging)");
|
||||
public ZigExecConfigBuild(@NotNull Project project, @NotNull ConfigurationFactory factory) {
|
||||
super(project, factory, "Zig Build");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] buildCommandLineArgs() {
|
||||
val base = new String[]{"build", "--color", colored ? "on" : "off"};
|
||||
if (extraArguments.isBlank()) {
|
||||
return base;
|
||||
} else {
|
||||
return Arrays.concat(base, extraArguments.split(" "));
|
||||
}
|
||||
val base = new String[]{"build", "--color", colored.colored ? "on" : "off"};
|
||||
return CollectionUtil.concat(base, extraArgs.args);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -66,53 +52,21 @@ public class ZigExecConfigBuild extends ZigExecConfigBase<ZigExecConfigBuild> im
|
|||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<ZigConfigEditor.ZigConfigModule<ZigExecConfigBuild>> getEditorConfigModules() {
|
||||
val arr = super.getEditorConfigModules();
|
||||
arr.add(new ExtraArgsModule());
|
||||
arr.add(new ZigConfigEditor.ColoredModule<>());
|
||||
return arr;
|
||||
public @NotNull List<ZigConfigEditor.ZigConfigurable<?>> getConfigurables() {
|
||||
return CollectionUtil.concat(super.getConfigurables(), extraArgs, colored, exePath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ZigExecConfigBuild clone() {
|
||||
val clone = super.clone();
|
||||
clone.extraArgs = extraArgs.clone();
|
||||
clone.colored = colored.clone();
|
||||
clone.exePath = exePath.clone();
|
||||
return clone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ProfileStateBuild getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) {
|
||||
return new ProfileStateBuild(environment, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readExternal(@NotNull Element element) throws InvalidDataException {
|
||||
super.readExternal(element);
|
||||
|
||||
ElementUtil.readString(element, "extraArguments").ifPresent(x -> extraArguments = x);
|
||||
ElementUtil.readBoolean(element, "colored").ifPresent(x -> colored = x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeExternal(@NotNull Element element) {
|
||||
super.writeExternal(element);
|
||||
|
||||
ElementUtil.writeString(element, "extraArguments", extraArguments);
|
||||
ElementUtil.writeBoolean(element, "colored", colored);
|
||||
}
|
||||
|
||||
public static class ExtraArgsModule implements ZigConfigEditor.ZigConfigModule<ZigExecConfigBuild> {
|
||||
private final JBTextField extraArgs = new JBTextField();
|
||||
|
||||
@Override
|
||||
public void applyTo(@NotNull ZigExecConfigBuild s) throws ConfigurationException {
|
||||
s.extraArguments = extraArgs.getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetFrom(@NotNull ZigExecConfigBuild s) {
|
||||
extraArgs.setText(Objects.requireNonNullElse(s.extraArguments, ""));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void construct(Panel p) {
|
||||
p.row("Extra arguments", (r) -> {
|
||||
r.cell(extraArgs).resizableColumn().align(AlignX.FILL).align(AlignY.FILL);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,9 @@ import com.intellij.openapi.vfs.VirtualFile;
|
|||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ConfigProducerRun extends ConfigProducerBase<ZigExecConfigRun> {
|
||||
@Override
|
||||
public @NotNull ConfigurationFactory getConfigurationFactory() {
|
||||
|
@ -30,9 +33,9 @@ public class ConfigProducerRun extends ConfigProducerBase<ZigExecConfigRun> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean setupConfigurationFromContext(@NotNull ZigExecConfigRun configuration, PsiElement element, String filePath, VirtualFile theFile) {
|
||||
protected boolean setupConfigurationFromContext(@NotNull ZigExecConfigRun configuration, PsiElement element, Path filePath, VirtualFile theFile) {
|
||||
if (ZigLineMarkerRun.UTILITY_INSTANCE.elementMatches(element)) {
|
||||
configuration.filePath = filePath;
|
||||
configuration.getFilePath().setPath(filePath);
|
||||
configuration.setName(theFile.getPresentableName());
|
||||
return true;
|
||||
}
|
||||
|
@ -40,8 +43,8 @@ public class ConfigProducerRun extends ConfigProducerBase<ZigExecConfigRun> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean isConfigurationFromContext(@NotNull ZigExecConfigRun configuration, String filePath, VirtualFile vFile, PsiElement element) {
|
||||
return configuration.filePath.equals(filePath);
|
||||
protected boolean isConfigurationFromContext(@NotNull ZigExecConfigRun configuration, Path filePath, VirtualFile vFile, PsiElement element) {
|
||||
return Objects.equals(configuration.getFilePath().getPath().orElse(null), filePath);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,36 +16,43 @@
|
|||
|
||||
package com.falsepattern.zigbrains.project.execution.run;
|
||||
|
||||
import com.falsepattern.zigbrains.common.util.CollectionUtil;
|
||||
import com.falsepattern.zigbrains.project.execution.base.ZigExecConfigBase;
|
||||
import com.falsepattern.zigbrains.project.execution.base.ZigConfigEditor;
|
||||
import com.falsepattern.zigbrains.project.util.ElementUtil;
|
||||
import com.intellij.execution.Executor;
|
||||
import com.intellij.execution.configurations.ConfigurationFactory;
|
||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.InvalidDataException;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.val;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public class ZigExecConfigRun extends ZigExecConfigBase<ZigExecConfigRun> implements
|
||||
ZigConfigEditor.FilePathModule.Carrier, ZigConfigEditor.ColoredModule.Carrier {
|
||||
public String filePath = "";
|
||||
public boolean colored = true;
|
||||
public class ZigExecConfigRun extends ZigExecConfigBase<ZigExecConfigRun> {
|
||||
private ZigConfigEditor.FilePathConfigurable filePath = new ZigConfigEditor.FilePathConfigurable("filePath", "File Path");
|
||||
private ZigConfigEditor.ColoredConfigurable colored = new ZigConfigEditor.ColoredConfigurable("colored");
|
||||
private ZigConfigEditor.OptimizationConfigurable optimization = new ZigConfigEditor.OptimizationConfigurable("optimization");
|
||||
private ZigConfigEditor.ArgsConfigurable exeArgs = new ZigConfigEditor.ArgsConfigurable("exeArgs", "Arguments for the compile exe");
|
||||
public ZigExecConfigRun(@NotNull Project project, @NotNull ConfigurationFactory factory) {
|
||||
super(project, factory, "Zig Run");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] buildCommandLineArgs() {
|
||||
return new String[]{"run", "--color", colored ? "on" : "off", filePath};
|
||||
return CollectionUtil.concat(new String[]{"run", "--color", colored.colored ? "on" : "off", filePath.getPathOrThrow().toString(), "-O", optimization.level.name(), "--"}, exeArgs.args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] buildDebugCommandLineArgs() {
|
||||
if (optimization.forced) {
|
||||
return new String[]{"build-exe", "--color", colored.colored ? "on" : "off", filePath.getPathOrThrow().toString(), "-O", optimization.level.name()};
|
||||
} else {
|
||||
return new String[]{"build-exe", "--color", colored.colored ? "on" : "off", filePath.getPathOrThrow().toString()};
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -54,31 +61,22 @@ public class ZigExecConfigRun extends ZigExecConfigBase<ZigExecConfigRun> implem
|
|||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<ZigConfigEditor.ZigConfigModule<ZigExecConfigRun>> getEditorConfigModules() {
|
||||
val modules = super.getEditorConfigModules();
|
||||
modules.add(new ZigConfigEditor.FilePathModule<>());
|
||||
modules.add(new ZigConfigEditor.ColoredModule<>());
|
||||
return modules;
|
||||
public ZigExecConfigRun clone() {
|
||||
val clone = super.clone();
|
||||
clone.filePath = filePath.clone();
|
||||
clone.colored = colored.clone();
|
||||
clone.optimization = optimization.clone();
|
||||
clone.exeArgs = exeArgs.clone();
|
||||
return clone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<ZigConfigEditor.ZigConfigurable<?>> getConfigurables() {
|
||||
return CollectionUtil.concat(super.getConfigurables(), filePath, optimization, colored);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ProfileStateRun getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) {
|
||||
return new ProfileStateRun(environment, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readExternal(@NotNull Element element) throws InvalidDataException {
|
||||
super.readExternal(element);
|
||||
|
||||
ElementUtil.readString(element, "filePath").ifPresent(x -> filePath = x);
|
||||
ElementUtil.readBoolean(element, "colored").ifPresent(x -> colored = x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeExternal(@NotNull Element element) {
|
||||
super.writeExternal(element);
|
||||
|
||||
ElementUtil.writeString(element, "filePath", filePath);
|
||||
ElementUtil.writeBoolean(element, "colored", colored);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,9 @@ import com.intellij.openapi.vfs.VirtualFile;
|
|||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ConfigProducerTest extends ConfigProducerBase<ZigExecConfigTest> {
|
||||
@Override
|
||||
public @NotNull ConfigurationFactory getConfigurationFactory() {
|
||||
|
@ -31,9 +34,9 @@ public class ConfigProducerTest extends ConfigProducerBase<ZigExecConfigTest> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean setupConfigurationFromContext(@NotNull ZigExecConfigTest configuration, PsiElement element, String filePath, VirtualFile theFile) {
|
||||
protected boolean setupConfigurationFromContext(@NotNull ZigExecConfigTest configuration, PsiElement element, Path filePath, VirtualFile theFile) {
|
||||
if (ZigLineMarkerTest.UTILITY_INSTANCE.elementMatches(element)) {
|
||||
configuration.filePath = filePath;
|
||||
configuration.getFilePath().setPath(filePath);
|
||||
configuration.setName("all tests in " + theFile.getPresentableName());
|
||||
return true;
|
||||
}
|
||||
|
@ -41,8 +44,8 @@ public class ConfigProducerTest extends ConfigProducerBase<ZigExecConfigTest> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean isConfigurationFromContext(@NotNull ZigExecConfigTest configuration, String filePath, VirtualFile vFile, PsiElement element) {
|
||||
return configuration.filePath.equals(filePath);
|
||||
protected boolean isConfigurationFromContext(@NotNull ZigExecConfigTest configuration, Path filePath, VirtualFile vFile, PsiElement element) {
|
||||
return Objects.equals(configuration.getFilePath().getPath().orElse(null), filePath);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,15 +16,15 @@
|
|||
|
||||
package com.falsepattern.zigbrains.project.execution.test;
|
||||
|
||||
import com.falsepattern.zigbrains.common.util.CollectionUtil;
|
||||
import com.falsepattern.zigbrains.project.execution.base.OptimizationLevel;
|
||||
import com.falsepattern.zigbrains.project.execution.base.ProfileStateBase;
|
||||
import com.falsepattern.zigbrains.project.execution.base.ZigExecConfigBase;
|
||||
import com.falsepattern.zigbrains.project.execution.base.ZigConfigEditor;
|
||||
import com.falsepattern.zigbrains.project.util.ElementUtil;
|
||||
import com.intellij.execution.Executor;
|
||||
import com.intellij.execution.configurations.ConfigurationFactory;
|
||||
import com.intellij.execution.configurations.RunConfiguration;
|
||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||
import com.intellij.openapi.options.SettingsEditor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.InvalidDataException;
|
||||
import lombok.Getter;
|
||||
|
@ -37,18 +37,26 @@ import org.jetbrains.annotations.Nullable;
|
|||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class ZigExecConfigTest extends ZigExecConfigBase<ZigExecConfigTest> implements ZigConfigEditor.FilePathModule.Carrier,
|
||||
ZigConfigEditor.ColoredModule.Carrier {
|
||||
public String filePath = "";
|
||||
public boolean colored = true;
|
||||
public class ZigExecConfigTest extends ZigExecConfigBase<ZigExecConfigTest> {
|
||||
private ZigConfigEditor.FilePathConfigurable filePath = new ZigConfigEditor.FilePathConfigurable("filePath", "File path");
|
||||
private ZigConfigEditor.ColoredConfigurable colored = new ZigConfigEditor.ColoredConfigurable("colored");
|
||||
private ZigConfigEditor.OptimizationConfigurable optimization = new ZigConfigEditor.OptimizationConfigurable("optimization");
|
||||
public ZigExecConfigTest(@NotNull Project project, @NotNull ConfigurationFactory factory) {
|
||||
super(project, factory, "Zig Test");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] buildCommandLineArgs() {
|
||||
return new String[]{"test", "--color", colored ? "on" : "off", filePath};
|
||||
return new String[]{"test", "--color", colored.colored ? "on" : "off", filePath.getPathOrThrow().toString(), "-O", optimization.level.name()};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] buildDebugCommandLineArgs() {
|
||||
if (optimization.forced) {
|
||||
return new String[]{"test", "--color", colored.colored ? "on" : "off", filePath.getPathOrThrow().toString(), "--test-no-exec", "-O", optimization.level.name()};
|
||||
} else {
|
||||
return new String[]{"test", "--color", colored.colored ? "on" : "off", filePath.getPathOrThrow().toString(), "--test-no-exec"};
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -62,26 +70,16 @@ public class ZigExecConfigTest extends ZigExecConfigBase<ZigExecConfigTest> impl
|
|||
}
|
||||
|
||||
@Override
|
||||
public void readExternal(@NotNull Element element) throws InvalidDataException {
|
||||
super.readExternal(element);
|
||||
|
||||
ElementUtil.readString(element, "filePath").ifPresent(x -> filePath = x);
|
||||
ElementUtil.readBoolean(element, "colored").ifPresent(x -> colored = x);
|
||||
public ZigExecConfigTest clone() {
|
||||
val clone = super.clone();
|
||||
clone.filePath = filePath.clone();
|
||||
clone.colored = colored.clone();
|
||||
clone.optimization = optimization.clone();
|
||||
return clone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeExternal(@NotNull Element element) {
|
||||
super.writeExternal(element);
|
||||
|
||||
ElementUtil.writeString(element, "filePath", filePath);
|
||||
ElementUtil.writeBoolean(element, "colored", colored);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<ZigConfigEditor.ZigConfigModule<ZigExecConfigTest>> getEditorConfigModules() {
|
||||
val modules = super.getEditorConfigModules();
|
||||
modules.add(new ZigConfigEditor.FilePathModule<>());
|
||||
modules.add(new ZigConfigEditor.ColoredModule<>());
|
||||
return modules;
|
||||
public @NotNull List<ZigConfigEditor.ZigConfigurable<?>> getConfigurables() {
|
||||
return CollectionUtil.concat(super.getConfigurables(), filePath, optimization, colored);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,6 @@ public class ZigRegularRunner extends ZigProgramRunnerBase<ProfileStateBase<?>>
|
|||
@Override
|
||||
protected @Nullable RunContentDescriptor doExecute(ProfileStateBase<?> state, AbstractZigToolchain toolchain, ExecutionEnvironment environment)
|
||||
throws ExecutionException {
|
||||
return DefaultProgramRunnerKt.showRunContent(state.executeCommandLine(state.getCommandLine(toolchain), environment), environment);
|
||||
return DefaultProgramRunnerKt.showRunContent(state.executeCommandLine(state.getCommandLine(toolchain, false), environment), environment);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,17 +17,24 @@
|
|||
package com.falsepattern.zigbrains.project.ui;
|
||||
|
||||
import com.intellij.execution.ExecutionBundle;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
|
||||
import com.intellij.openapi.ui.LabeledComponent;
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||
|
||||
public class WorkingDirectoryComponent extends LabeledComponent<TextFieldWithBrowseButton> {
|
||||
public WorkingDirectoryComponent() {
|
||||
var component = new TextFieldWithBrowseButton();
|
||||
public class WorkingDirectoryComponent extends LabeledComponent<TextFieldWithBrowseButton> implements Disposable {
|
||||
private final TextFieldWithBrowseButton field;
|
||||
public WorkingDirectoryComponent(Disposable parent) {
|
||||
field = new TextFieldWithBrowseButton(null, parent);
|
||||
var fileChooser = FileChooserDescriptorFactory.createSingleFolderDescriptor();
|
||||
fileChooser.setTitle(ExecutionBundle.message("select.working.directory.message"));
|
||||
component.addBrowseFolderListener(null, null, null, fileChooser);
|
||||
setComponent(component);
|
||||
field.addBrowseFolderListener(null, null, null, fileChooser);
|
||||
setComponent(field);
|
||||
setText(ExecutionBundle.message("run.configuration.working.directory.label"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
field.dispose();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,12 +20,16 @@ import com.falsepattern.zigbrains.project.execution.ZigCapturingProcessHandler;
|
|||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.intellij.execution.process.CapturingProcessHandler;
|
||||
import com.intellij.execution.process.ProcessOutput;
|
||||
import com.intellij.openapi.options.ConfigurationException;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import lombok.val;
|
||||
import org.apache.tools.ant.BuildException;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Optional;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
public class CLIUtil {
|
||||
public static Optional<ProcessOutput> execute(GeneralCommandLine cli, int timeoutMillis) {
|
||||
|
@ -48,4 +52,72 @@ public class CLIUtil {
|
|||
return handler.runProcess();
|
||||
}
|
||||
}
|
||||
|
||||
//From Apache Ant
|
||||
/**
|
||||
* Crack a command line.
|
||||
* @param toProcess the command line to process.
|
||||
* @return the command line broken into strings.
|
||||
* An empty or null toProcess parameter results in a zero sized array.
|
||||
*/
|
||||
public static String[] translateCommandline(String toProcess) throws ConfigurationException {
|
||||
if (toProcess == null || toProcess.isEmpty()) {
|
||||
//no command? no string
|
||||
return new String[0];
|
||||
}
|
||||
// parse with a simple finite state machine
|
||||
|
||||
final int normal = 0;
|
||||
final int inQuote = 1;
|
||||
final int inDoubleQuote = 2;
|
||||
int state = normal;
|
||||
final StringTokenizer tok = new StringTokenizer(toProcess, "\"' ", true);
|
||||
final ArrayList<String> result = new ArrayList<>();
|
||||
final StringBuilder current = new StringBuilder();
|
||||
boolean lastTokenHasBeenQuoted = false;
|
||||
|
||||
while (tok.hasMoreTokens()) {
|
||||
String nextTok = tok.nextToken();
|
||||
switch (state) {
|
||||
case inQuote:
|
||||
if ("'".equals(nextTok)) {
|
||||
lastTokenHasBeenQuoted = true;
|
||||
state = normal;
|
||||
} else {
|
||||
current.append(nextTok);
|
||||
}
|
||||
break;
|
||||
case inDoubleQuote:
|
||||
if ("\"".equals(nextTok)) {
|
||||
lastTokenHasBeenQuoted = true;
|
||||
state = normal;
|
||||
} else {
|
||||
current.append(nextTok);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if ("'".equals(nextTok)) {
|
||||
state = inQuote;
|
||||
} else if ("\"".equals(nextTok)) {
|
||||
state = inDoubleQuote;
|
||||
} else if (" ".equals(nextTok)) {
|
||||
if (lastTokenHasBeenQuoted || current.length() > 0) {
|
||||
result.add(current.toString());
|
||||
current.setLength(0);
|
||||
}
|
||||
} else {
|
||||
current.append(nextTok);
|
||||
}
|
||||
lastTokenHasBeenQuoted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (lastTokenHasBeenQuoted || current.length() > 0) {
|
||||
result.add(current.toString());
|
||||
}
|
||||
if (state == inQuote || state == inDoubleQuote) {
|
||||
throw new ConfigurationException("unbalanced quotes in " + toProcess);
|
||||
}
|
||||
return result.toArray(new String[0]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,8 +18,8 @@ package com.falsepattern.zigbrains.project.util;
|
|||
|
||||
import lombok.val;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public class ElementUtil {
|
||||
|
@ -34,10 +34,52 @@ public class ElementUtil {
|
|||
.map(it -> it.getAttributeValue("value"));
|
||||
}
|
||||
|
||||
public static Optional<Boolean> readBoolean(Element element, String name) {
|
||||
return readString(element, name).map(Boolean::parseBoolean);
|
||||
}
|
||||
|
||||
public static <T extends Enum<T>> Optional<T> readEnum(Element element, String name, Class<T> enumClass) {
|
||||
return readString(element, name).map(value -> {
|
||||
try {
|
||||
val field = enumClass.getDeclaredField(value);
|
||||
//noinspection unchecked
|
||||
return (T) field.get(null);
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static Optional<Element> readChild(Element element, String name) {
|
||||
return element.getChildren()
|
||||
.stream()
|
||||
.filter(it -> it.getName()
|
||||
.equals("ZigBrainsNestedOption") &&
|
||||
it.getAttributeValue("name")
|
||||
.equals(name))
|
||||
.findAny();
|
||||
}
|
||||
|
||||
public static Optional<String[]> readStrings(Element element, String name) {
|
||||
return element.getChildren()
|
||||
.stream()
|
||||
.filter(it -> it.getName()
|
||||
.equals("ZigBrainsArrayOption") &&
|
||||
it.getAttributeValue("name")
|
||||
.equals(name))
|
||||
.findAny()
|
||||
.map(it -> it.getChildren()
|
||||
.stream()
|
||||
.filter(it2 -> it2.getName()
|
||||
.equals("ZigBrainsArrayEntry"))
|
||||
.map(it2 -> it2.getAttributeValue("value"))
|
||||
.toArray(String[]::new));
|
||||
}
|
||||
|
||||
public static void writeString(Element element, String name, String value) {
|
||||
val option = new Element("ZigBrainsOption");
|
||||
option.setAttribute("name", name);
|
||||
option.setAttribute("value", value);
|
||||
option.setAttribute("value", Objects.requireNonNullElse(value, ""));
|
||||
|
||||
element.addContent(option);
|
||||
}
|
||||
|
@ -46,7 +88,25 @@ public class ElementUtil {
|
|||
writeString(element, name, Boolean.toString(state));
|
||||
}
|
||||
|
||||
public static Optional<Boolean> readBoolean(Element element, String name) {
|
||||
return readString(element, name).map(Boolean::parseBoolean);
|
||||
public static <T extends Enum<T>> void writeEnum(Element element, String name, T value) {
|
||||
writeString(element, name, value.name());
|
||||
}
|
||||
|
||||
public static void writeStrings(Element element, String name, String... values) {
|
||||
val arr = new Element("ZigBrainsArrayOption");
|
||||
arr.setAttribute("name", name);
|
||||
for (val value: values) {
|
||||
val subElem = new Element("ZigBrainsArrayEntry");
|
||||
subElem.setAttribute("value", value);
|
||||
arr.addContent(subElem);
|
||||
}
|
||||
element.addContent(arr);
|
||||
}
|
||||
|
||||
public static Element writeChild(Element element, String name) {
|
||||
val child = new Element("ZigBrainsNestedOption");
|
||||
child.setAttribute("name", name);
|
||||
element.addContent(child);
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
<runLineMarkerContributor language="Zig"
|
||||
implementationClass="com.falsepattern.zigbrains.project.execution.build.ZigLineMarkerBuild"/>
|
||||
|
||||
<configurationType implementation="com.falsepattern.zigbrains.project.execution.binary.ConfigTypeBinary"/>
|
||||
|
||||
<directoryProjectGenerator implementation="com.falsepattern.zigbrains.project.platform.ZigDirectoryProjectGenerator"/>
|
||||
<newProjectWizard.languageGenerator implementation="com.falsepattern.zigbrains.project.ide.newproject.ZigNewProjectWizard"/>
|
||||
|
||||
|
|
BIN
modules/zig/src/main/resources/coconut.jpg
Normal file
BIN
modules/zig/src/main/resources/coconut.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.6 KiB |
Loading…
Add table
Reference in a new issue