backport: master
fix: Local variables now show up in the debugger properly chore: The flattening chore: Remove unnecessary plugin xmls docs: Update module tree chore: Remove flexmark, replace with builtin intellij apis fix: wrong lsp4j package (used IJ shipped lsp4j instead of ours) (cherry picked from commit49483a9f9c
) ci: fix deps (cherry picked from commit1ff8616d26
) chore: better project path management (cherry picked from commit0ebbe1cc9b
) ci: Slightly better git version management (cherry picked from commit6fbf826574
) chore: Optimize imports (cherry picked from commit92c5e57a03
) chore: Require restart unconditionally (cherry picked from commit9e90502b05
) docs: Update readme (cherry picked from commit95799c627b
) docs: Update changelog (cherry picked from commit45b153f7c4
) feat: Better configurability (cherry picked from commitec5c07c0a1
) fix: Annotator jank breaking diagnostics (cherry picked from commit85bd68393c
) chore: move some misplaced xml entires (cherry picked from commit7b2ad7c324
) feat!: Huge debugging refactor - Debug support for Windows - Debuggable zig build - Debuggable binaries This is a squashed commit so ignore the weird author date (cherry picked from commit3d0dbb8e36
) fix: NPE in go to definition (cherry picked from commitb4539c0aa9
) fix: Builds on windows again (cherry picked from commit733f0b2622
) feat: Improved docs, more reliable file sync (cherry picked from commit23b72086bc
) chore: LanguageServerDefinition remove ancient obsolete logic (cherry picked from commit8a0c862446
) chore: Move lsp connection logic to lsp-common (cherry picked from commit3287051e3d
) chore: Move ApplicationUtil to common (cherry picked from commit845af09e29
) feat!: Colored builds and clickable file path references (cherry picked from commit66aef224b2
)
This commit is contained in:
parent
ff29b663dc
commit
59d4dcc8bf
114 changed files with 5178 additions and 882 deletions
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -22,6 +22,7 @@ Changelog structure reference:
|
|||
|
||||
- Debugging
|
||||
- Debugging support for tests when launched using the ZigTest task type (and with the gutter icons in the editor)
|
||||
- Debugging support on Windows systems
|
||||
|
||||
- Project
|
||||
- Added `zig init` as a new project creation option
|
||||
|
@ -30,6 +31,9 @@ Changelog structure reference:
|
|||
- Zig
|
||||
- Updated semantic highlighting to latest ZLS protocol
|
||||
|
||||
- ZLS
|
||||
- ZLS configuration is now partially editable through the GUI
|
||||
|
||||
### Fixed
|
||||
|
||||
- Project
|
||||
|
@ -38,6 +42,14 @@ Changelog structure reference:
|
|||
- Plugin
|
||||
- Removed a bunch of write action locking, the editor should feel more responsive now
|
||||
|
||||
- Zig
|
||||
- Error highlighting was breaking all the time
|
||||
|
||||
### Removed
|
||||
|
||||
- Project
|
||||
- !!!BREAKING CHANGE!!! There is now no arbitrary "zig execution" task, all zig tasks have been categorized into Zig run/build/test tasks respectively.
|
||||
|
||||
## [12.0.0]
|
||||
|
||||
### Added
|
||||
|
|
|
@ -21,10 +21,13 @@ IC modules MUST NOT depend on CL modules, as this violates the restrictions set
|
|||
|
||||
### Common (IC)
|
||||
|
||||
### LSP (IC)
|
||||
### LSP-Common (IC)
|
||||
- LSP4J (EXT)
|
||||
- Flexmark (EXT)
|
||||
|
||||
### LSP (IC)
|
||||
- Apache Commons Lang 3 (EXT)
|
||||
- Common (IC)
|
||||
- LSP-Common (IC)
|
||||
|
||||
### Zig (IC)
|
||||
- Grammarkit (EXT)
|
||||
|
@ -39,6 +42,8 @@ IC modules MUST NOT depend on CL modules, as this violates the restrictions set
|
|||
- Grammarkit (EXT)
|
||||
- Common (IC)
|
||||
|
||||
### Debugger (CL)
|
||||
### Debugger (IU/CL)
|
||||
- Common (IC)
|
||||
- LSP-Common (IC)
|
||||
- Zig (IC)
|
||||
- Project (IC)
|
16
README.md
16
README.md
|
@ -13,11 +13,12 @@ complain about missing files
|
|||
|
||||
## Special Thanks
|
||||
|
||||
- The [ZigTools](https://github.com/zigtools/) team for developing the Zig Language Server.
|
||||
- [HTGAzureX1212](https://github.com/HTGAzureX1212) for developing [intellij-zig](https://github.com/intellij-zig/intellij-zig),
|
||||
which served as a fantastic reference for deep IDE integration features
|
||||
|
||||
- The members of the `Zig Programming Language` discord server's `#tooling-dev` channel for providing encouragement and
|
||||
feedback
|
||||
- The members of the `Zig Programming Language` discord server's `#tooling-dev` channel for providing encouragement,
|
||||
feedback, and lots of bug reports.
|
||||
|
||||
- The Ballerina Platform developers for `lsp4intellij`, the language server connector between the IntelliJ platform
|
||||
and the Eclipse LSP4J project
|
||||
|
@ -80,7 +81,16 @@ LSP server is running.
|
|||
|
||||
## Debugging
|
||||
|
||||
ZigBrains uses the CLion C++ toolchains (Settings | Build, Execution, Deployment | Toolchains) for debugging purposes,
|
||||
### Windows
|
||||
|
||||
Due to technical limitations, the C++ toolchains cannot be used for debugging zig code on windows.
|
||||
|
||||
Go to `Settings | Build, Execution, Deployment | Debugger | Zig (Windows)` and follow the steps shown there to set up a
|
||||
zig-compatible debugger.
|
||||
|
||||
### Linux / MacOS / Unix
|
||||
|
||||
ZigBrains uses the CLion C++ toolchains `Settings | Build, Execution, Deployment | Toolchains` for debugging purposes,
|
||||
and it is fully compatible with both GDB and LLDB debuggers.
|
||||
|
||||
Additionally, ZigBrains will prioritize a toolchain if it is called `Zig`, otherwise it will use the default toolchain.
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
>
|
||||
<defs
|
||||
id="defs36">
|
||||
<linearGradient
|
||||
|
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 9.2 KiB |
|
@ -43,15 +43,26 @@ tasks {
|
|||
}
|
||||
}
|
||||
|
||||
fun pluginVersion(): Provider<String> {
|
||||
fun pluginVersionGit(): Provider<String> {
|
||||
return provider {
|
||||
System.getenv("RELEASE_VERSION")
|
||||
}.orElse(provider {
|
||||
try {
|
||||
gitVersion()
|
||||
} catch (_: java.lang.Exception) {
|
||||
error("Git version not found and RELEASE_VERSION environment variable is not set!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun pluginVersion(): Provider<String> {
|
||||
return provider {
|
||||
System.getenv("RELEASE_VERSION")
|
||||
}.orElse(pluginVersionGit().map {
|
||||
val suffix = "-" + properties("pluginSinceBuild").get()
|
||||
if (it.endsWith(suffix)) {
|
||||
it.substring(0, it.length - suffix.length)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -97,6 +108,11 @@ allprojects {
|
|||
targetCompatibility = javaVersion
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile::class) {
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
|
||||
|
||||
group = properties("pluginGroup").get()
|
||||
version = pluginVersionFull().get()
|
||||
|
||||
|
@ -169,6 +185,10 @@ project(":debugger") {
|
|||
dependencies {
|
||||
implementation(project(":zig"))
|
||||
implementation(project(":project"))
|
||||
implementation(project(":common"))
|
||||
implementation(project(":lsp-common"))
|
||||
implementation(project(":lsp"))
|
||||
implementation("org.eclipse.lsp4j:org.eclipse.lsp4j.debug:0.22.0")
|
||||
}
|
||||
intellij {
|
||||
version = clionVersion
|
||||
|
@ -176,13 +196,22 @@ project(":debugger") {
|
|||
}
|
||||
}
|
||||
|
||||
project(":lsp-common") {
|
||||
apply {
|
||||
plugin("java-library")
|
||||
}
|
||||
dependencies {
|
||||
api("org.eclipse.lsp4j:org.eclipse.lsp4j:0.22.0")
|
||||
}
|
||||
}
|
||||
|
||||
project(":lsp") {
|
||||
apply {
|
||||
plugin("java-library")
|
||||
}
|
||||
dependencies {
|
||||
api("org.eclipse.lsp4j:org.eclipse.lsp4j:0.22.0")
|
||||
implementation("com.vladsch.flexmark:flexmark:0.64.8")
|
||||
implementation(project(":common"))
|
||||
api(project(":lsp-common"))
|
||||
api("org.apache.commons:commons-lang3:3.14.0")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,18 +13,17 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.falsepattern.zigbrains.lsp.utils;
|
||||
package com.falsepattern.zigbrains.common.util;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ex.ApplicationManagerEx;
|
||||
import com.intellij.openapi.project.NoAccessDuringPsiEvents;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import lombok.val;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class ApplicationUtils {
|
||||
public class ApplicationUtil {
|
||||
|
||||
private final static ExecutorService EXECUTOR_SERVICE;
|
||||
|
||||
|
@ -50,16 +49,7 @@ public class ApplicationUtils {
|
|||
}
|
||||
|
||||
static public <T> T computableReadAction(Computable<T> computable) {
|
||||
if (ApplicationManager.getApplication().isDispatchThread() ||
|
||||
ApplicationManagerEx.getApplicationEx().holdsReadLock()) {
|
||||
return ApplicationManager.getApplication().runReadAction(computable);
|
||||
} else {
|
||||
var result = new Object() {
|
||||
T value = null;
|
||||
};
|
||||
ApplicationManager.getApplication().invokeAndWait(() -> result.value = ApplicationManager.getApplication().runReadAction(computable));
|
||||
return result.value;
|
||||
}
|
||||
return ApplicationManager.getApplication().runReadAction(computable);
|
||||
}
|
||||
|
||||
static public void writeAction(Runnable runnable) {
|
||||
|
@ -70,15 +60,15 @@ public class ApplicationUtils {
|
|||
return ApplicationManager.getApplication().runWriteAction(computable);
|
||||
}
|
||||
|
||||
static public void invokeAfterPsiEvents(Runnable runnable) {
|
||||
static public void invokeAfterPsiEvents(Runnable runnable, boolean readLock, boolean writeLock) {
|
||||
Runnable wrapper = () -> {
|
||||
if (NoAccessDuringPsiEvents.isInsideEventProcessing()) {
|
||||
invokeAfterPsiEvents(runnable);
|
||||
invokeAfterPsiEvents(runnable, readLock, writeLock);
|
||||
} else {
|
||||
runnable.run();
|
||||
}
|
||||
};
|
||||
|
||||
ApplicationManager.getApplication().invokeLater(wrapper, (Condition<Void>) value -> false);
|
||||
val app = ApplicationManager.getApplication();
|
||||
app.invokeLater(writeLock ? wrapper : () -> app.executeOnPooledThread(readLock ? () -> app.runReadAction(wrapper) : wrapper));
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* 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 com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class FileUtil {
|
||||
private static final Logger LOG = Logger.getInstance(FileUtil.class);
|
||||
|
||||
public final static String SPACE_ENCODED = "%20";
|
||||
private final static OS os = (System.getProperty("os.name").toLowerCase().contains("win")) ? OS.WINDOWS : OS.UNIX;
|
||||
private final static String COLON_ENCODED = "%3A";
|
||||
private final static String URI_FILE_BEGIN = "file:";
|
||||
private final static String URI_VALID_FILE_BEGIN = "file:///";
|
||||
private final static char URI_PATH_SEP = '/';
|
||||
|
||||
/**
|
||||
* Fixes common problems in uri, mainly related to Windows
|
||||
*
|
||||
* @param uri The uri to sanitize
|
||||
* @return The sanitized uri
|
||||
*/
|
||||
public static String sanitizeURI(String uri) {
|
||||
if (uri != null) {
|
||||
StringBuilder reconstructed = new StringBuilder();
|
||||
String uriCp = uri.replaceAll(" ", SPACE_ENCODED); //Don't trust servers
|
||||
if (!uri.startsWith(URI_FILE_BEGIN)) {
|
||||
LOG.warn("Malformed uri : " + uri);
|
||||
return uri; //Probably not an uri
|
||||
} else {
|
||||
uriCp = uriCp.substring(URI_FILE_BEGIN.length());
|
||||
while (uriCp.startsWith(Character.toString(URI_PATH_SEP))) {
|
||||
uriCp = uriCp.substring(1);
|
||||
}
|
||||
reconstructed.append(URI_VALID_FILE_BEGIN);
|
||||
if (os == OS.UNIX) {
|
||||
return reconstructed.append(uriCp).toString();
|
||||
} else {
|
||||
reconstructed.append(uriCp.substring(0, uriCp.indexOf(URI_PATH_SEP)));
|
||||
char driveLetter = reconstructed.charAt(URI_VALID_FILE_BEGIN.length());
|
||||
if (Character.isLowerCase(driveLetter)) {
|
||||
reconstructed.setCharAt(URI_VALID_FILE_BEGIN.length(), Character.toUpperCase(driveLetter));
|
||||
}
|
||||
if (reconstructed.toString().endsWith(COLON_ENCODED)) {
|
||||
reconstructed.delete(reconstructed.length() - 3, reconstructed.length());
|
||||
}
|
||||
if (!reconstructed.toString().endsWith(":")) {
|
||||
reconstructed.append(":");
|
||||
}
|
||||
return reconstructed.append(uriCp.substring(uriCp.indexOf(URI_PATH_SEP))).toString();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URI string corresponding to a VirtualFileSystem file
|
||||
*
|
||||
* @param file The file
|
||||
* @return the URI
|
||||
*/
|
||||
public static String URIFromVirtualFile(VirtualFile file) {
|
||||
return file == null? null : pathToUri(file.getPath());
|
||||
}
|
||||
/**
|
||||
* Transforms an URI string into a VFS file
|
||||
*
|
||||
* @param uri The uri
|
||||
* @return The virtual file
|
||||
*/
|
||||
public static VirtualFile virtualFileFromURI(URI uri) {
|
||||
return LocalFileSystem.getInstance().findFileByIoFile(new File(uri));
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms an URI string into a VFS file
|
||||
*
|
||||
* @param uri The uri
|
||||
* @return The virtual file
|
||||
*/
|
||||
public static VirtualFile virtualFileFromURI(String uri) {
|
||||
try {
|
||||
return virtualFileFromURI(new URI(sanitizeURI(uri)));
|
||||
} catch (URISyntaxException e) {
|
||||
LOG.warn(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a path into an URI string
|
||||
*
|
||||
* @param path The path
|
||||
* @return The uri
|
||||
*/
|
||||
public static String pathToUri(@Nullable String path) {
|
||||
return path != null ? sanitizeURI(new File(path).toURI().toString()) : null;
|
||||
}
|
||||
|
||||
public static String pathToUri(@Nullable Path path) {
|
||||
return path != null ? sanitizeURI(path.toUri().toString()) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Object representing the OS type (Windows or Unix)
|
||||
*/
|
||||
public enum OS {
|
||||
WINDOWS, UNIX
|
||||
}
|
||||
}
|
|
@ -16,6 +16,10 @@
|
|||
|
||||
package com.falsepattern.zigbrains.common.util;
|
||||
|
||||
import lombok.val;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class StringUtil {
|
||||
public static String blankToNull(String value) {
|
||||
return value == null || value.isBlank() ? null : value;
|
||||
|
@ -24,4 +28,56 @@ public class StringUtil {
|
|||
public static String orEmpty(String value) {
|
||||
return value == null ? "" : value;
|
||||
}
|
||||
|
||||
|
||||
private static final char[] VT100_CHARS = new char[256];
|
||||
|
||||
static {
|
||||
Arrays.fill(VT100_CHARS, ' ');
|
||||
VT100_CHARS[0x6A] = '┘';
|
||||
VT100_CHARS[0x6B] = '┐';
|
||||
VT100_CHARS[0x6C] = '┌';
|
||||
VT100_CHARS[0x6D] = '└';
|
||||
VT100_CHARS[0x6E] = '┼';
|
||||
VT100_CHARS[0x71] = '─';
|
||||
VT100_CHARS[0x74] = '├';
|
||||
VT100_CHARS[0x75] = '┤';
|
||||
VT100_CHARS[0x76] = '┴';
|
||||
VT100_CHARS[0x77] = '┬';
|
||||
VT100_CHARS[0x78] = '│';
|
||||
}
|
||||
|
||||
private static final String VT100_BEGIN_SEQ = "\u001B(0";
|
||||
private static final String VT100_END_SEQ = "\u001B(B";
|
||||
private static final int VT100_BEGIN_SEQ_LENGTH = VT100_BEGIN_SEQ.length();
|
||||
private static final int VT100_END_SEQ_LENGTH = VT100_END_SEQ.length();
|
||||
|
||||
public static String translateVT100Escapes(String text) {
|
||||
int offset = 0;
|
||||
val result = new StringBuilder();
|
||||
val textLength = text.length();
|
||||
while (offset < textLength) {
|
||||
val startIndex = text.indexOf(VT100_BEGIN_SEQ, offset);
|
||||
if (startIndex < 0) {
|
||||
result.append(text.substring(offset, textLength).replace(VT100_END_SEQ, ""));
|
||||
break;
|
||||
}
|
||||
result.append(text, offset, startIndex);
|
||||
val blockOffset = startIndex + VT100_BEGIN_SEQ_LENGTH;
|
||||
var endIndex = text.indexOf(VT100_END_SEQ, blockOffset);
|
||||
if (endIndex < 0) {
|
||||
endIndex = textLength;
|
||||
}
|
||||
for (int i = blockOffset; i < endIndex; i++) {
|
||||
val c = text.charAt(i);
|
||||
if (c >= 256) {
|
||||
result.append(c);
|
||||
} else {
|
||||
result.append(VT100_CHARS[c]);
|
||||
}
|
||||
}
|
||||
offset = endIndex + VT100_END_SEQ_LENGTH;
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,17 +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.
|
||||
-->
|
||||
|
||||
<idea-plugin/>
|
1
modules/debugger/.gitignore
vendored
Normal file
1
modules/debugger/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
!src/**/build/
|
|
@ -14,9 +14,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.falsepattern.zigbrains.zig.cpp;
|
||||
package com.falsepattern.zigbrains.cpp;
|
||||
|
||||
import com.falsepattern.zigbrains.zig.debugbridge.DebuggerDriverProvider;
|
||||
import com.falsepattern.zigbrains.debugbridge.DebuggerDriverProvider;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.jetbrains.cidr.cpp.execution.debugger.backend.CLionGDBDriverConfiguration;
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.falsepattern.zigbrains.zig.debugbridge;
|
||||
package com.falsepattern.zigbrains.debugbridge;
|
||||
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.project.Project;
|
|
@ -14,13 +14,15 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.falsepattern.zigbrains.zig.debugger;
|
||||
package com.falsepattern.zigbrains.debugger;
|
||||
|
||||
import com.falsepattern.zigbrains.zig.debugbridge.DebuggerDriverProvider;
|
||||
import com.falsepattern.zigbrains.debugbridge.DebuggerDriverProvider;
|
||||
import com.falsepattern.zigbrains.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)
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.falsepattern.zigbrains.zig.debugger;
|
||||
package com.falsepattern.zigbrains.debugger;
|
||||
|
||||
import com.jetbrains.cidr.execution.debugger.CidrDebuggerEditorsExtensionBase;
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.falsepattern.zigbrains.zig.debugger;
|
||||
package com.falsepattern.zigbrains.debugger;
|
||||
|
||||
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriver;
|
||||
import lombok.AccessLevel;
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.falsepattern.zigbrains.zig.debugger;
|
||||
package com.falsepattern.zigbrains.debugger;
|
||||
|
||||
import com.falsepattern.zigbrains.project.execution.base.ZigExecConfigBase;
|
||||
import com.intellij.execution.configurations.RunProfile;
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.falsepattern.zigbrains.zig.debugger;
|
||||
package com.falsepattern.zigbrains.debugger;
|
||||
|
||||
import com.falsepattern.zigbrains.zig.ZigFileType;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.falsepattern.zigbrains.zig.debugger;
|
||||
package com.falsepattern.zigbrains.debugger;
|
||||
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.filters.Filter;
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright 2023-2024 FalsePattern
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.falsepattern.zigbrains.debugger;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.xdebugger.XSourcePosition;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.LLValue;
|
||||
import com.jetbrains.cidr.execution.debugger.evaluation.LocalVariablesFilterHandler;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class ZigVariablesFilterHandler implements LocalVariablesFilterHandler {
|
||||
@NotNull
|
||||
@Override
|
||||
public CompletableFuture<List<LLValue>> filterVars(@NotNull Project project, @NotNull XSourcePosition xSourcePosition, @NotNull List<? extends LLValue> list) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
val vf = xSourcePosition.getFile();
|
||||
if ("zig".equals(vf.getExtension())) {
|
||||
return new ArrayList<>(list);
|
||||
}
|
||||
return List.of();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canFilterAtPos(@NotNull Project proj, @NotNull XSourcePosition pos) {
|
||||
return "zig".equals(pos.getFile().getExtension());
|
||||
}
|
||||
}
|
|
@ -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.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.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.debugger.dap;
|
||||
|
||||
import com.falsepattern.zigbrains.common.util.ApplicationUtil;
|
||||
import com.falsepattern.zigbrains.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.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.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.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.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,13 +14,13 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.falsepattern.zigbrains.zig.debugger.base;
|
||||
package com.falsepattern.zigbrains.debugger.runner.base;
|
||||
|
||||
import com.falsepattern.zigbrains.project.execution.base.ProfileStateBase;
|
||||
import com.falsepattern.zigbrains.project.runconfig.ZigProgramRunnerBase;
|
||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||
import com.falsepattern.zigbrains.zig.debugger.Utils;
|
||||
import com.falsepattern.zigbrains.zig.debugger.ZigLocalDebugProcess;
|
||||
import com.falsepattern.zigbrains.debugger.Utils;
|
||||
import com.falsepattern.zigbrains.debugger.ZigLocalDebugProcess;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.RunProfile;
|
||||
import com.intellij.execution.executors.DefaultDebugExecutor;
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright 2023-2024 FalsePattern
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.falsepattern.zigbrains.debugger.runner.binary;
|
||||
|
||||
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugParametersBase;
|
||||
import com.falsepattern.zigbrains.project.execution.binary.ProfileStateBinary;
|
||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||
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;
|
||||
|
||||
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.debugger.runner.binary;
|
||||
|
||||
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugParametersBase;
|
||||
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugRunnerBase;
|
||||
import com.falsepattern.zigbrains.project.execution.base.ProfileStateBase;
|
||||
import com.falsepattern.zigbrains.project.execution.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.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.debugger.runner.build;
|
||||
|
||||
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugParametersBase;
|
||||
import com.falsepattern.zigbrains.project.execution.build.ProfileStateBuild;
|
||||
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 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.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.debugger.runner.base.ZigDebugParametersBase;
|
||||
import com.falsepattern.zigbrains.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.debugger.runner.run;
|
||||
|
||||
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugEmitBinaryInstaller;
|
||||
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugParametersBase;
|
||||
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.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.debugger.runner.run;
|
||||
|
||||
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugRunnerBase;
|
||||
import com.falsepattern.zigbrains.project.execution.base.ProfileStateBase;
|
||||
import com.falsepattern.zigbrains.project.execution.run.ProfileStateRun;
|
||||
import com.falsepattern.zigbrains.project.execution.run.ZigExecConfigRun;
|
||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||
import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain;
|
||||
import com.falsepattern.zigbrains.zig.debugger.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.debugger.runner.test;
|
||||
|
||||
import com.falsepattern.zigbrains.project.execution.test.ProfileStateTest;
|
||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugEmitBinaryInstaller;
|
||||
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugParametersBase;
|
||||
import com.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.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.debugger.runner.base.ZigDebugParametersBase;
|
||||
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugRunnerBase;
|
||||
import com.intellij.execution.configurations.RunProfile;
|
||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||
import com.intellij.notification.Notification;
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* Copyright 2023-2024 FalsePattern
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.falsepattern.zigbrains.debugger.win;
|
||||
|
||||
import com.falsepattern.zigbrains.debugger.dap.DAPDriver;
|
||||
import com.falsepattern.zigbrains.debugger.dap.WrappedDebugServer;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.util.system.CpuArch;
|
||||
import com.jetbrains.cidr.ArchitectureType;
|
||||
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.jsonrpc.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.debugger.win;
|
||||
|
||||
import com.falsepattern.zigbrains.debugger.dap.DAPDebuggerDriverConfiguration;
|
||||
import com.falsepattern.zigbrains.debugger.win.config.WinDebuggerConfigService;
|
||||
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.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,88 @@
|
|||
/*
|
||||
* Copyright 2023-2024 FalsePattern
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.falsepattern.zigbrains.debugger.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.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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,10 +14,10 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<idea-plugin package="com.falsepattern.zigbrains.zig.cpp">
|
||||
<idea-plugin package="com.falsepattern.zigbrains.cpp">
|
||||
<depends>com.intellij.modules.clion</depends>
|
||||
<extensions defaultExtensionNs="com.falsepattern.zigbrains">
|
||||
<debuggerDriverProvider implementation="com.falsepattern.zigbrains.zig.cpp.CPPDebuggerDriverProvider"/>
|
||||
<debuggerDriverProvider implementation="com.falsepattern.zigbrains.cpp.CPPDebuggerDriverProvider"/>
|
||||
</extensions>
|
||||
|
||||
</idea-plugin>
|
|
@ -14,29 +14,37 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<idea-plugin package="com.falsepattern.zigbrains.zig.debugger">
|
||||
<idea-plugin package="com.falsepattern.zigbrains.debugger">
|
||||
<depends>com.intellij.modules.cidr.debugger</depends>
|
||||
<resource-bundle>zigbrains.zig.debugger.Bundle</resource-bundle>
|
||||
<resource-bundle>zigbrains.debugger.Bundle</resource-bundle>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<programRunner implementation="com.falsepattern.zigbrains.zig.debugger.run.ZigDebugRunnerRun"
|
||||
<programRunner implementation="com.falsepattern.zigbrains.debugger.runner.run.ZigDebugRunnerRun"
|
||||
id="ZigDebugRunnerRun"/>
|
||||
<programRunner implementation="com.falsepattern.zigbrains.zig.debugger.test.ZigDebugRunnerTest"
|
||||
<programRunner implementation="com.falsepattern.zigbrains.debugger.runner.test.ZigDebugRunnerTest"
|
||||
id="ZigDebugRunnerTest"/>
|
||||
<programRunner implementation="com.falsepattern.zigbrains.debugger.runner.build.ZigDebugRunnerBuild"
|
||||
id="ZigDebugRunnerBuild"/>
|
||||
<programRunner implementation="com.falsepattern.zigbrains.debugger.runner.binary.ZigDebugRunnerBinary"
|
||||
id="ZigDebugRunnerBinary"/>
|
||||
|
||||
<notificationGroup displayType="BALLOON"
|
||||
bundle="zigbrains.zig.debugger.Bundle"
|
||||
bundle="zigbrains.debugger.Bundle"
|
||||
key="notif-debug-error"
|
||||
id="ZigBrains.Debugger.Error"/>
|
||||
<notificationGroup displayType="BALLOON"
|
||||
bundle="zigbrains.zig.debugger.Bundle"
|
||||
bundle="zigbrains.debugger.Bundle"
|
||||
key="notif-debug-warn"
|
||||
id="ZigBrains.Debugger.Warn"/>
|
||||
<applicationConfigurable parentId="project.propDebugger"
|
||||
instance="com.falsepattern.zigbrains.debugger.win.config.WinDebuggerConfigurable"
|
||||
displayName="Zig (Windows)"/>
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="cidr.debugger">
|
||||
<languageSupport language="Zig" implementationClass="com.falsepattern.zigbrains.zig.debugger.ZigDebuggerLanguageSupport"/>
|
||||
<editorsExtension language="Zig" implementationClass="com.falsepattern.zigbrains.zig.debugger.ZigDebuggerEditorsExtension"/>
|
||||
<lineBreakpointFileTypesProvider implementation="com.falsepattern.zigbrains.zig.debugger.ZigLineBreakpointFileTypesProvider"/>
|
||||
<languageSupport language="Zig" implementationClass="com.falsepattern.zigbrains.debugger.ZigDebuggerLanguageSupport"/>
|
||||
<editorsExtension language="Zig" implementationClass="com.falsepattern.zigbrains.debugger.ZigDebuggerEditorsExtension"/>
|
||||
<lineBreakpointFileTypesProvider implementation="com.falsepattern.zigbrains.debugger.ZigLineBreakpointFileTypesProvider"/>
|
||||
<localVariablesFilterHandler implementation="com.falsepattern.zigbrains.debugger.ZigVariablesFilterHandler"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.falsepattern.zigbrains.lsp.client.connection;
|
||||
package com.falsepattern.zigbrains.lspcommon.connection;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
@ -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) {
|
|
@ -13,8 +13,9 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.falsepattern.zigbrains.lsp.client.connection;
|
||||
package com.falsepattern.zigbrains.lspcommon.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();
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package com.falsepattern.zigbrains.lsp;
|
||||
|
||||
import com.falsepattern.zigbrains.common.util.FileUtil;
|
||||
import com.falsepattern.zigbrains.lsp.client.languageserver.ServerStatus;
|
||||
import com.falsepattern.zigbrains.lsp.client.languageserver.serverdefinition.LanguageServerDefinition;
|
||||
import com.falsepattern.zigbrains.lsp.client.languageserver.wrapper.LanguageServerWrapper;
|
||||
|
@ -27,6 +28,7 @@ import com.intellij.openapi.editor.Editor;
|
|||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import lombok.val;
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
import org.apache.commons.lang3.tuple.MutablePair;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
@ -43,7 +45,7 @@ import java.util.Set;
|
|||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static com.falsepattern.zigbrains.lsp.utils.ApplicationUtils.pool;
|
||||
import static com.falsepattern.zigbrains.common.util.ApplicationUtil.pool;
|
||||
|
||||
public class IntellijLanguageClient {
|
||||
|
||||
|
@ -330,9 +332,14 @@ public class IntellijLanguageClient {
|
|||
public static void removeWrapper(LanguageServerWrapper wrapper) {
|
||||
if (wrapper.getProject() != null) {
|
||||
String[] extensions = wrapper.getServerDefinition().ext.split(LanguageServerDefinition.SPLIT_CHAR);
|
||||
val rootPath = wrapper.getProjectRootPath();
|
||||
if (rootPath == null) {
|
||||
LOG.error("Project root path is null");
|
||||
return;
|
||||
}
|
||||
val absolutePath = FileUtil.pathToUri(rootPath);
|
||||
for (String ext : extensions) {
|
||||
MutablePair<String, String> extProjectPair = new MutablePair<>(ext, FileUtils.pathToUri(
|
||||
new File(wrapper.getProjectRootPath()).getAbsolutePath()));
|
||||
MutablePair<String, String> extProjectPair = new MutablePair<>(ext, absolutePath);
|
||||
extToLanguageWrapper.remove(extProjectPair);
|
||||
extToServerDefinition.remove(extProjectPair);
|
||||
}
|
||||
|
|
|
@ -19,13 +19,10 @@ package com.falsepattern.zigbrains.lsp.actions;
|
|||
import com.falsepattern.zigbrains.lsp.IntellijLanguageClient;
|
||||
import com.falsepattern.zigbrains.lsp.editor.EditorEventManager;
|
||||
import com.falsepattern.zigbrains.lsp.editor.EditorEventManagerBase;
|
||||
import com.falsepattern.zigbrains.lsp.requests.ReformatHandler;
|
||||
import com.falsepattern.zigbrains.lsp.utils.ApplicationUtils;
|
||||
import com.intellij.codeInsight.navigation.actions.GotoDeclarationAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiFile;
|
||||
|
|
|
@ -20,13 +20,9 @@ import com.falsepattern.zigbrains.lsp.IntellijLanguageClient;
|
|||
import com.falsepattern.zigbrains.lsp.editor.EditorEventManager;
|
||||
import com.falsepattern.zigbrains.lsp.editor.EditorEventManagerBase;
|
||||
import com.intellij.codeInsight.hint.actions.ShowImplementationsAction;
|
||||
import com.intellij.codeInsight.navigation.CtrlMouseAction;
|
||||
import com.intellij.codeInsight.navigation.actions.GotoDeclarationAction;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.DumbAware;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiFile;
|
||||
|
@ -58,6 +54,8 @@ public class LSPGotoDefinitionAction extends ShowImplementationsAction {
|
|||
super.actionPerformed(e);
|
||||
return;
|
||||
}
|
||||
manager.gotoDefinition(psiElement);
|
||||
if (!manager.gotoDefinition(psiElement)) {
|
||||
super.actionPerformed(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,14 +15,12 @@
|
|||
*/
|
||||
package com.falsepattern.zigbrains.lsp.actions;
|
||||
|
||||
import com.falsepattern.zigbrains.common.util.ApplicationUtil;
|
||||
import com.falsepattern.zigbrains.lsp.IntellijLanguageClient;
|
||||
import com.falsepattern.zigbrains.lsp.requests.ReformatHandler;
|
||||
import com.falsepattern.zigbrains.lsp.utils.ApplicationUtils;
|
||||
import com.intellij.codeInsight.actions.ReformatCodeAction;
|
||||
import com.intellij.lang.LanguageFormatting;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
|
@ -51,7 +49,7 @@ public class LSPReformatAction extends ReformatCodeAction implements DumbAware {
|
|||
super.actionPerformed(e);
|
||||
return;
|
||||
}
|
||||
ApplicationUtils.writeAction(() -> FileDocumentManager.getInstance().saveDocument(editor.getDocument()));
|
||||
ApplicationUtil.writeAction(() -> FileDocumentManager.getInstance().saveDocument(editor.getDocument()));
|
||||
// if editor hasSelection, only reformat selection, not reformat the whole file
|
||||
if (editor.getSelectionModel().hasSelection()) {
|
||||
ReformatHandler.reformatSelection(editor);
|
||||
|
|
|
@ -22,7 +22,6 @@ import com.intellij.codeInsight.actions.LayoutCodeDialog;
|
|||
import com.intellij.codeInsight.actions.LayoutCodeOptions;
|
||||
import com.intellij.codeInsight.actions.ShowReformatFileDialog;
|
||||
import com.intellij.codeInsight.actions.TextRangeType;
|
||||
import com.intellij.lang.LanguageFormatting;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
|
|
|
@ -15,10 +15,10 @@
|
|||
*/
|
||||
package com.falsepattern.zigbrains.lsp.client;
|
||||
|
||||
import com.falsepattern.zigbrains.common.util.ApplicationUtil;
|
||||
import com.falsepattern.zigbrains.common.util.FileUtil;
|
||||
import com.falsepattern.zigbrains.lsp.editor.EditorEventManagerBase;
|
||||
import com.falsepattern.zigbrains.lsp.requests.WorkspaceEditHandler;
|
||||
import com.falsepattern.zigbrains.lsp.utils.ApplicationUtils;
|
||||
import com.falsepattern.zigbrains.lsp.utils.FileUtils;
|
||||
import com.intellij.notification.Notification;
|
||||
import com.intellij.notification.NotificationAction;
|
||||
import com.intellij.notification.NotificationGroup;
|
||||
|
@ -123,7 +123,7 @@ public class DefaultLanguageClient implements LanguageClient {
|
|||
|
||||
@Override
|
||||
public void publishDiagnostics(PublishDiagnosticsParams publishDiagnosticsParams) {
|
||||
String uri = FileUtils.sanitizeURI(publishDiagnosticsParams.getUri());
|
||||
String uri = FileUtil.sanitizeURI(publishDiagnosticsParams.getUri());
|
||||
List<Diagnostic> diagnostics = publishDiagnosticsParams.getDiagnostics();
|
||||
EditorEventManagerBase.diagnostics(uri, diagnostics);
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ public class DefaultLanguageClient implements LanguageClient {
|
|||
String message = messageParams.getMessage();
|
||||
|
||||
if (isModal) {
|
||||
ApplicationUtils.invokeLater(() -> {
|
||||
ApplicationUtil.invokeLater(() -> {
|
||||
MessageType msgType = messageParams.getType();
|
||||
switch (msgType) {
|
||||
case Error:
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package com.falsepattern.zigbrains.lsp.client.languageserver.serverdefinition;
|
||||
|
||||
import com.falsepattern.zigbrains.lsp.client.connection.StreamConnectionProvider;
|
||||
import com.falsepattern.zigbrains.lspcommon.connection.StreamConnectionProvider;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
@ -24,7 +24,6 @@ import org.eclipse.lsp4j.InitializeParams;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
@ -32,7 +31,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
/**
|
||||
* A trait representing a ServerDefinition
|
||||
*/
|
||||
public class LanguageServerDefinition {
|
||||
public abstract class LanguageServerDefinition {
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(LanguageServerDefinition.class);
|
||||
|
||||
|
@ -75,18 +74,6 @@ public class LanguageServerDefinition {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the initialization options for the given uri.
|
||||
*
|
||||
* @param uri file URI
|
||||
* @return initialization options
|
||||
* @deprecated use {@link #customizeInitializeParams(InitializeParams)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public Object getInitializationOptions(URI uri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to modify the {@link InitializeParams} that was initialized by this library. The values
|
||||
* assigned to the passed {@link InitializeParams} after this method ends will be the ones sent to the LSP server.
|
||||
|
@ -107,9 +94,7 @@ public class LanguageServerDefinition {
|
|||
* @param workingDir The root directory
|
||||
* @return The stream connection provider
|
||||
*/
|
||||
public StreamConnectionProvider createConnectionProvider(String workingDir) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
public abstract StreamConnectionProvider createConnectionProvider(String workingDir);
|
||||
|
||||
public ServerListener getServerListener() {
|
||||
return ServerListener.DEFAULT;
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
*/
|
||||
package com.falsepattern.zigbrains.lsp.client.languageserver.serverdefinition;
|
||||
|
||||
import com.falsepattern.zigbrains.lsp.client.connection.ProcessStreamConnectionProvider;
|
||||
import com.falsepattern.zigbrains.lsp.client.connection.StreamConnectionProvider;
|
||||
import com.falsepattern.zigbrains.lspcommon.connection.ProcessStreamConnectionProvider;
|
||||
import com.falsepattern.zigbrains.lspcommon.connection.StreamConnectionProvider;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
*/
|
||||
package com.falsepattern.zigbrains.lsp.client.languageserver.serverdefinition;
|
||||
|
||||
import com.falsepattern.zigbrains.lsp.client.connection.ProcessStreamConnectionProvider;
|
||||
import com.falsepattern.zigbrains.lsp.client.connection.StreamConnectionProvider;
|
||||
import com.falsepattern.zigbrains.lspcommon.connection.ProcessStreamConnectionProvider;
|
||||
import com.falsepattern.zigbrains.lspcommon.connection.StreamConnectionProvider;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package com.falsepattern.zigbrains.lsp.client.languageserver.wrapper;
|
||||
|
||||
import com.falsepattern.zigbrains.common.util.ApplicationUtil;
|
||||
import com.falsepattern.zigbrains.common.util.FileUtil;
|
||||
import com.falsepattern.zigbrains.lsp.IntellijLanguageClient;
|
||||
import com.falsepattern.zigbrains.lsp.client.DefaultLanguageClient;
|
||||
import com.falsepattern.zigbrains.lsp.client.ServerWrapperBaseClientContext;
|
||||
|
@ -23,7 +25,6 @@ import com.falsepattern.zigbrains.lsp.client.languageserver.ServerStatus;
|
|||
import com.falsepattern.zigbrains.lsp.client.languageserver.requestmanager.DefaultRequestManager;
|
||||
import com.falsepattern.zigbrains.lsp.client.languageserver.requestmanager.RequestManager;
|
||||
import com.falsepattern.zigbrains.lsp.client.languageserver.serverdefinition.LanguageServerDefinition;
|
||||
import com.falsepattern.zigbrains.lsp.editor.DocumentEventManager;
|
||||
import com.falsepattern.zigbrains.lsp.editor.EditorEventManager;
|
||||
import com.falsepattern.zigbrains.lsp.editor.EditorEventManagerBase;
|
||||
import com.falsepattern.zigbrains.lsp.extensions.LSPExtensionManager;
|
||||
|
@ -33,7 +34,6 @@ import com.falsepattern.zigbrains.lsp.requests.Timeout;
|
|||
import com.falsepattern.zigbrains.lsp.requests.Timeouts;
|
||||
import com.falsepattern.zigbrains.lsp.statusbar.LSPServerStatusWidget;
|
||||
import com.falsepattern.zigbrains.lsp.statusbar.LSPServerStatusWidgetFactory;
|
||||
import com.falsepattern.zigbrains.lsp.utils.ApplicationUtils;
|
||||
import com.falsepattern.zigbrains.lsp.utils.FileUtils;
|
||||
import com.falsepattern.zigbrains.lsp.utils.LSPException;
|
||||
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
|
||||
|
@ -43,6 +43,7 @@ import com.intellij.openapi.editor.Editor;
|
|||
import com.intellij.openapi.fileEditor.FileEditor;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectUtil;
|
||||
import com.intellij.openapi.ui.MessageType;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
|
@ -51,6 +52,7 @@ import com.intellij.psi.PsiDocumentManager;
|
|||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.remoteServer.util.CloudNotifier;
|
||||
import com.intellij.util.PlatformIcons;
|
||||
import lombok.val;
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.eclipse.lsp4j.ClientCapabilities;
|
||||
|
@ -104,6 +106,7 @@ import java.io.InputStream;
|
|||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -131,12 +134,12 @@ 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<>();
|
||||
private final String projectRootPath;
|
||||
@Nullable
|
||||
private final Path projectRootPath;
|
||||
private final HashSet<String> urisUnderLspControl = new HashSet<>();
|
||||
private final HashSet<Editor> connectedEditors = new HashSet<>();
|
||||
private final Map<String, Set<EditorEventManager>> uriToEditorManagers = new HashMap<>();
|
||||
|
@ -167,7 +170,12 @@ public class LanguageServerWrapper {
|
|||
this.project = project;
|
||||
// We need to keep the project rootPath in addition to the project instance, since we cannot get the project
|
||||
// base path if the project is disposed.
|
||||
this.projectRootPath = project.getBasePath();
|
||||
val projectDir = ProjectUtil.guessProjectDir(project);
|
||||
if (projectDir != null) {
|
||||
this.projectRootPath = projectDir.toNioPath();
|
||||
} else {
|
||||
this.projectRootPath = null;
|
||||
}
|
||||
this.extManager = extManager;
|
||||
projectToLanguageServerWrapper.put(project, this);
|
||||
}
|
||||
|
@ -182,7 +190,7 @@ public class LanguageServerWrapper {
|
|||
}
|
||||
|
||||
public static LanguageServerWrapper forVirtualFile(VirtualFile file, Project project) {
|
||||
return uriToLanguageServerWrapper.get(new ImmutablePair<>(FileUtils.VFSToURI(file), FileUtils.projectToUri(project)));
|
||||
return uriToLanguageServerWrapper.get(new ImmutablePair<>(FileUtil.URIFromVirtualFile(file), FileUtils.projectToUri(project)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -201,7 +209,7 @@ public class LanguageServerWrapper {
|
|||
return serverDefinition;
|
||||
}
|
||||
|
||||
public String getProjectRootPath() {
|
||||
public Path getProjectRootPath() {
|
||||
return projectRootPath;
|
||||
}
|
||||
|
||||
|
@ -237,7 +245,7 @@ public class LanguageServerWrapper {
|
|||
String msg = String.format("%s \n is not initialized after %d seconds",
|
||||
serverDefinition.toString(), Timeout.getTimeout(Timeouts.INIT) / 1000);
|
||||
LOG.warn(msg, e);
|
||||
ApplicationUtils.invokeLater(() -> {
|
||||
ApplicationUtil.invokeLater(() -> {
|
||||
if (!alreadyShownTimeout) {
|
||||
notifier.showMessage(msg, MessageType.WARNING);
|
||||
alreadyShownTimeout = true;
|
||||
|
@ -280,7 +288,7 @@ public class LanguageServerWrapper {
|
|||
return null;
|
||||
}
|
||||
VirtualFile currentOpenFile = selectedEditor.getFile();
|
||||
VirtualFile requestedFile = FileUtils.virtualFileFromURI(uri);
|
||||
VirtualFile requestedFile = FileUtil.virtualFileFromURI(uri);
|
||||
if (currentOpenFile == null || requestedFile == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -396,7 +404,7 @@ public class LanguageServerWrapper {
|
|||
}
|
||||
// Triggers annotators since this is the first editor which starts the LS
|
||||
// and annotators are executed before LS is bootstrap to provide diagnostics.
|
||||
ApplicationUtils.computableReadAction(() -> {
|
||||
ApplicationUtil.computableReadAction(() -> {
|
||||
PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
|
||||
if (psiFile != null) {
|
||||
DaemonCodeAnalyzer.getInstance(project).restart(psiFile);
|
||||
|
@ -449,7 +457,7 @@ public class LanguageServerWrapper {
|
|||
launcherFuture.cancel(true);
|
||||
}
|
||||
if (serverDefinition != null) {
|
||||
serverDefinition.stop(projectRootPath);
|
||||
serverDefinition.stop(projectRootPath != null ? projectRootPath.toString() : null);
|
||||
}
|
||||
for (Editor ed : new HashSet<>(connectedEditors)) {
|
||||
disconnect(ed);
|
||||
|
@ -496,7 +504,7 @@ public class LanguageServerWrapper {
|
|||
if (status == STOPPED && !alreadyShownCrash && !alreadyShownTimeout) {
|
||||
setStatus(STARTING);
|
||||
try {
|
||||
Pair<InputStream, OutputStream> streams = serverDefinition.start(projectRootPath);
|
||||
Pair<InputStream, OutputStream> streams = serverDefinition.start(projectRootPath != null ? projectRootPath.toString() : null);
|
||||
InputStream inputStream = streams.getKey();
|
||||
OutputStream outputStream = streams.getValue();
|
||||
InitializeParams initParams = getInitParams();
|
||||
|
@ -540,7 +548,7 @@ public class LanguageServerWrapper {
|
|||
});
|
||||
} catch (LSPException | IOException | URISyntaxException e) {
|
||||
LOG.warn(e);
|
||||
ApplicationUtils.invokeLater(() ->
|
||||
ApplicationUtil.invokeLater(() ->
|
||||
notifier.showMessage(String.format("Can't start server due to %s", e.getMessage()),
|
||||
MessageType.WARNING));
|
||||
removeServerWrapper();
|
||||
|
@ -550,7 +558,7 @@ public class LanguageServerWrapper {
|
|||
|
||||
private InitializeParams getInitParams() throws URISyntaxException {
|
||||
InitializeParams initParams = new InitializeParams();
|
||||
String projectRootUri = FileUtils.pathToUri(projectRootPath);
|
||||
String projectRootUri = FileUtil.pathToUri(projectRootPath);
|
||||
WorkspaceFolder workspaceFolder = new WorkspaceFolder(projectRootUri, this.project.getName());
|
||||
initParams.setWorkspaceFolders(Collections.singletonList(workspaceFolder));
|
||||
|
||||
|
@ -591,8 +599,6 @@ public class LanguageServerWrapper {
|
|||
new ClientCapabilities(workspaceClientCapabilities, textDocumentClientCapabilities, null));
|
||||
initParams.setClientInfo(new ClientInfo(ApplicationInfo.getInstance().getVersionName(), ApplicationInfo.getInstance().getFullVersion()));
|
||||
|
||||
// custom initialization options and initialize params provided by users
|
||||
initParams.setInitializationOptions(serverDefinition.getInitializationOptions(URI.create(initParams.getWorkspaceFolders().get(0).getUri())));
|
||||
serverDefinition.customizeInitializeParams(initParams);
|
||||
return initParams;
|
||||
}
|
||||
|
@ -639,7 +645,7 @@ public class LanguageServerWrapper {
|
|||
if (crashCount <= 3) {
|
||||
reconnect();
|
||||
} else {
|
||||
ApplicationUtils.invokeLater(() -> {
|
||||
ApplicationUtil.invokeLater(() -> {
|
||||
if (alreadyShownCrash) {
|
||||
reconnect();
|
||||
} else {
|
||||
|
@ -679,7 +685,7 @@ public class LanguageServerWrapper {
|
|||
List<String> connected = new ArrayList<>();
|
||||
urisUnderLspControl.forEach(s -> {
|
||||
try {
|
||||
connected.add(new URI(FileUtils.sanitizeURI(s)).toString());
|
||||
connected.add(new URI(FileUtil.sanitizeURI(s)).toString());
|
||||
} catch (URISyntaxException e) {
|
||||
LOG.warn(e);
|
||||
}
|
||||
|
@ -733,7 +739,7 @@ public class LanguageServerWrapper {
|
|||
* @param projectUri The project root uri
|
||||
*/
|
||||
public void disconnect(String uri, String projectUri) {
|
||||
uriToLanguageServerWrapper.remove(new ImmutablePair<>(FileUtils.sanitizeURI(uri), FileUtils.sanitizeURI(projectUri)));
|
||||
uriToLanguageServerWrapper.remove(new ImmutablePair<>(FileUtil.sanitizeURI(uri), FileUtil.sanitizeURI(projectUri)));
|
||||
|
||||
Set<EditorEventManager> managers = uriToEditorManagers.get(uri);
|
||||
if (managers == null) {
|
||||
|
@ -752,7 +758,7 @@ public class LanguageServerWrapper {
|
|||
}
|
||||
}
|
||||
urisUnderLspControl.remove(uri);
|
||||
uriToLanguageServerWrapper.remove(new ImmutablePair<>(FileUtils.sanitizeURI(uri), FileUtils.sanitizeURI(projectUri)));
|
||||
uriToLanguageServerWrapper.remove(new ImmutablePair<>(FileUtil.sanitizeURI(uri), FileUtil.sanitizeURI(projectUri)));
|
||||
}
|
||||
if (connectedEditors.isEmpty()) {
|
||||
stop(true);
|
||||
|
@ -787,7 +793,7 @@ public class LanguageServerWrapper {
|
|||
* Reset language server wrapper state so it can be started again if it was failed earlier.
|
||||
*/
|
||||
public void restart() {
|
||||
ApplicationUtils.pool(() -> {
|
||||
ApplicationUtil.pool(() -> {
|
||||
if (isRestartable()) {
|
||||
alreadyShownCrash = false;
|
||||
alreadyShownTimeout = false;
|
||||
|
|
|
@ -16,13 +16,14 @@
|
|||
|
||||
package com.falsepattern.zigbrains.lsp.contributors;
|
||||
|
||||
import com.falsepattern.zigbrains.common.util.ApplicationUtil;
|
||||
import com.falsepattern.zigbrains.lsp.editor.EditorEventManagerBase;
|
||||
import com.falsepattern.zigbrains.lsp.requests.HoverHandler;
|
||||
import com.falsepattern.zigbrains.lsp.requests.Timeout;
|
||||
import com.falsepattern.zigbrains.lsp.requests.Timeouts;
|
||||
import com.falsepattern.zigbrains.lsp.utils.ApplicationUtils;
|
||||
import com.falsepattern.zigbrains.lsp.utils.DocumentUtils;
|
||||
import com.falsepattern.zigbrains.lsp.utils.FileUtils;
|
||||
import com.intellij.markdown.utils.doc.DocMarkdownToHtmlConverter;
|
||||
import com.intellij.model.Pointer;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
|
@ -33,6 +34,7 @@ import com.intellij.platform.backend.presentation.TargetPresentation;
|
|||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.SmartPointerManager;
|
||||
import com.intellij.psi.SmartPsiFileRange;
|
||||
import lombok.val;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.eclipse.lsp4j.HoverParams;
|
||||
import org.eclipse.lsp4j.jsonrpc.JsonRpcException;
|
||||
|
@ -44,7 +46,6 @@ import java.util.List;
|
|||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class LSPDocumentationTargetProvider implements DocumentationTargetProvider {
|
||||
@Override
|
||||
|
@ -86,7 +87,7 @@ public class LSPDocumentationTargetProvider implements DocumentationTargetProvid
|
|||
return null;
|
||||
}
|
||||
var caretPos = editor.offsetToLogicalPosition(offset);
|
||||
var serverPos = ApplicationUtils.computableReadAction(() -> DocumentUtils.logicalToLSPPos(caretPos, editor));
|
||||
var serverPos = ApplicationUtil.computableReadAction(() -> DocumentUtils.logicalToLSPPos(caretPos, editor));
|
||||
return DocumentationResult.asyncDocumentation(() -> {
|
||||
var identifier = manager.getIdentifier();
|
||||
var request = wrapper.getRequestManager().hover(new HoverParams(identifier, serverPos));
|
||||
|
@ -102,13 +103,15 @@ public class LSPDocumentationTargetProvider implements DocumentationTargetProvid
|
|||
return null;
|
||||
}
|
||||
|
||||
String string = HoverHandler.getHoverString(hover);
|
||||
val markdown = HoverHandler.getHoverString(hover);
|
||||
val string = ApplicationUtil.computableReadAction(() -> DocMarkdownToHtmlConverter
|
||||
.convert(manager.getProject(), markdown));
|
||||
if (StringUtils.isEmpty(string)) {
|
||||
LOG.warn(String.format("Hover string returned is empty for file %s and pos (%d;%d)",
|
||||
identifier.getUri(), serverPos.getLine(), serverPos.getCharacter()));
|
||||
return null;
|
||||
}
|
||||
return DocumentationResult.documentation(string.lines().collect(Collectors.joining("<br>\n")));
|
||||
return DocumentationResult.documentation(string);
|
||||
} catch (TimeoutException e) {
|
||||
LOG.warn(e);
|
||||
wrapper.notifyFailure(Timeouts.HOVER);
|
||||
|
|
|
@ -25,11 +25,10 @@ import com.intellij.codeInsight.hints.declarative.InlayTreeSink;
|
|||
import com.intellij.codeInsight.hints.declarative.InlineInlayPosition;
|
||||
import com.intellij.codeInsight.hints.declarative.OwnBypassCollector;
|
||||
import com.intellij.codeInsight.hints.declarative.impl.DeclarativeInlayHintsPassFactory;
|
||||
import com.intellij.markdown.utils.doc.DocMarkdownToHtmlConverter;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.vladsch.flexmark.html.HtmlRenderer;
|
||||
import com.vladsch.flexmark.parser.Parser;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
@ -92,12 +91,10 @@ public class LSPInlayHintProvider implements InlayHintsProvider {
|
|||
tooltipText = switch (markup.getKind()) {
|
||||
case "markdown" -> {
|
||||
var markedContent = markup.getValue();
|
||||
if (markedContent.isEmpty()) {
|
||||
if (markedContent.isBlank()) {
|
||||
yield "";
|
||||
}
|
||||
Parser parser = Parser.builder().build();
|
||||
HtmlRenderer renderer = HtmlRenderer.builder().build();
|
||||
yield "<html>" + renderer.render(parser.parse(markedContent)) + "</html>";
|
||||
yield DocMarkdownToHtmlConverter.convert(manager.getProject(), markedContent);
|
||||
}
|
||||
default -> markup.getValue();
|
||||
};
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package com.falsepattern.zigbrains.lsp.contributors.annotator;
|
||||
|
||||
import com.falsepattern.zigbrains.common.util.FileUtil;
|
||||
import com.falsepattern.zigbrains.lsp.IntellijLanguageClient;
|
||||
import com.falsepattern.zigbrains.lsp.client.languageserver.ServerStatus;
|
||||
import com.falsepattern.zigbrains.lsp.client.languageserver.wrapper.LanguageServerWrapper;
|
||||
|
@ -32,8 +33,10 @@ import com.intellij.openapi.diagnostic.Logger;
|
|||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.util.SmartList;
|
||||
import lombok.val;
|
||||
import org.eclipse.lsp4j.Diagnostic;
|
||||
import org.eclipse.lsp4j.DiagnosticSeverity;
|
||||
import org.eclipse.lsp4j.DiagnosticTag;
|
||||
|
@ -103,7 +106,7 @@ public class LSPAnnotator extends ExternalAnnotator<Object, Object> {
|
|||
|
||||
VirtualFile virtualFile = file.getVirtualFile();
|
||||
if (FileUtils.isFileSupported(virtualFile) && IntellijLanguageClient.isExtensionSupported(virtualFile)) {
|
||||
String uri = FileUtils.VFSToURI(virtualFile);
|
||||
String uri = FileUtil.URIFromVirtualFile(virtualFile);
|
||||
// TODO annotations are applied to a file / document not to an editor. so store them by file and not by editor..
|
||||
EditorEventManager eventManager = EditorEventManagerBase.forUri(uri);
|
||||
if (eventManager == null) {
|
||||
|
@ -161,14 +164,17 @@ public class LSPAnnotator extends ExternalAnnotator<Object, Object> {
|
|||
}
|
||||
|
||||
@Nullable
|
||||
protected AnnotationBuilder createAnnotation(Editor editor, AnnotationHolder holder, Diagnostic diagnostic) {
|
||||
protected TextRange getTextRange(Editor editor, Diagnostic diagnostic) {
|
||||
final int start = DocumentUtils.LSPPosToOffset(editor, diagnostic.getRange().getStart());
|
||||
final int end = DocumentUtils.LSPPosToOffset(editor, diagnostic.getRange().getEnd());
|
||||
if (start >= end) {
|
||||
if (start > end) {
|
||||
return null;
|
||||
}
|
||||
final TextRange range = new TextRange(start, end);
|
||||
return new TextRange(start, end);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected AnnotationBuilder createAnnotation(@NotNull TextRange range, AnnotationHolder holder, Diagnostic diagnostic) {
|
||||
return holder.newAnnotation(lspToIntellijAnnotationsMap.get(diagnostic.getSeverity()), diagnostic.getMessage())
|
||||
.range(range);
|
||||
}
|
||||
|
@ -176,18 +182,47 @@ public class LSPAnnotator extends ExternalAnnotator<Object, Object> {
|
|||
private void createAnnotations(AnnotationHolder holder, EditorEventManager eventManager) {
|
||||
final List<Diagnostic> diagnostics = eventManager.getDiagnostics();
|
||||
final Editor editor = eventManager.editor;
|
||||
PsiFile file;
|
||||
val document = editor.getDocument();
|
||||
if (document != null) {
|
||||
file = PsiDocumentManager.getInstance(eventManager.getProject()).getPsiFile(document);
|
||||
} else {
|
||||
file = null;
|
||||
}
|
||||
|
||||
List<Annotation> annotations = new ArrayList<>();
|
||||
diagnostics.forEach(d -> {
|
||||
var annotation = createAnnotation(editor, holder, d);
|
||||
if (annotation != null) {
|
||||
if (d.getTags() != null && d.getTags().contains(DiagnosticTag.Deprecated)) {
|
||||
annotation = annotation.highlightType(ProblemHighlightType.LIKE_DEPRECATED);
|
||||
var range = getTextRange(editor, d);
|
||||
if (range == null)
|
||||
return;
|
||||
blk:
|
||||
if (file != null && range.getLength() == 0) {
|
||||
val psiElement = file.findElementAt(range.getStartOffset());
|
||||
if (psiElement != null && !psiElement.getText().equals(".")) {
|
||||
range = psiElement.getTextRange();
|
||||
break blk;
|
||||
}
|
||||
val psiElementForward = file.findElementAt(range.getStartOffset() + 1);
|
||||
if (psiElementForward != null) {
|
||||
range = psiElementForward.getTextRange();
|
||||
break blk;
|
||||
}
|
||||
val psiElementBack = file.findElementAt(range.getStartOffset() - 1);
|
||||
if (psiElementBack != null) {
|
||||
range = psiElementBack.getTextRange();
|
||||
break blk;
|
||||
}
|
||||
if (psiElement != null) {
|
||||
range = psiElement.getTextRange();
|
||||
}
|
||||
annotation.create();
|
||||
var theList = (SmartList<Annotation>) holder;
|
||||
annotations.add(theList.get(theList.size() - 1));
|
||||
}
|
||||
var annotation = createAnnotation(range, holder, d);
|
||||
if (d.getTags() != null && d.getTags().contains(DiagnosticTag.Deprecated)) {
|
||||
annotation = annotation.highlightType(ProblemHighlightType.LIKE_DEPRECATED);
|
||||
}
|
||||
annotation.create();
|
||||
var theList = (SmartList<Annotation>) holder;
|
||||
annotations.add(theList.get(theList.size() - 1));
|
||||
});
|
||||
|
||||
eventManager.setAnnotations(annotations);
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package com.falsepattern.zigbrains.lsp.contributors.psi;
|
||||
|
||||
import com.falsepattern.zigbrains.lsp.utils.ApplicationUtils;
|
||||
import com.falsepattern.zigbrains.common.util.ApplicationUtil;
|
||||
import com.falsepattern.zigbrains.lsp.utils.FileUtils;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.lang.Language;
|
||||
|
@ -700,7 +700,7 @@ public class LSPPsiElement extends PsiElementBase implements PsiNameIdentifierOw
|
|||
if (editor == null) {
|
||||
OpenFileDescriptor descriptor = new OpenFileDescriptor(getProject(), getContainingFile().getVirtualFile(),
|
||||
getTextOffset());
|
||||
ApplicationUtils.invokeLater(() -> ApplicationUtils
|
||||
ApplicationUtil.invokeLater(() -> ApplicationUtil
|
||||
.writeAction(() -> FileEditorManager.getInstance(getProject()).openTextEditor(descriptor, false)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package com.falsepattern.zigbrains.lsp.contributors.symbol;
|
||||
|
||||
import com.falsepattern.zigbrains.common.util.FileUtil;
|
||||
import com.falsepattern.zigbrains.lsp.IntellijLanguageClient;
|
||||
import com.falsepattern.zigbrains.lsp.client.languageserver.ServerStatus;
|
||||
import com.falsepattern.zigbrains.lsp.client.languageserver.requestmanager.RequestManager;
|
||||
|
@ -70,7 +71,7 @@ public class WorkspaceSymbolProvider {
|
|||
final SymbolInformation information = (result.getSymbolInformation() != null) ?
|
||||
result.getSymbolInformation() : from(result.getWorkspaceSymbol());
|
||||
final Location location = information.getLocation();
|
||||
final VirtualFile file = FileUtils.URIToVFS(location.getUri());
|
||||
final VirtualFile file = FileUtil.virtualFileFromURI(location.getUri());
|
||||
|
||||
if (file != null) {
|
||||
final LSPIconProvider iconProviderFor = GUIUtils.getIconProviderFor(result.getDefinition());
|
||||
|
|
|
@ -15,23 +15,18 @@
|
|||
*/
|
||||
package com.falsepattern.zigbrains.lsp.editor;
|
||||
|
||||
import com.falsepattern.zigbrains.common.util.ApplicationUtil;
|
||||
import com.falsepattern.zigbrains.lsp.client.languageserver.wrapper.LanguageServerWrapper;
|
||||
import com.falsepattern.zigbrains.lsp.utils.ApplicationUtils;
|
||||
import com.falsepattern.zigbrains.lsp.utils.DocumentUtils;
|
||||
import com.falsepattern.zigbrains.lsp.utils.FileUtils;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.event.DocumentEvent;
|
||||
import com.intellij.openapi.editor.event.DocumentListener;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.util.io.FileUtilRt;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import org.eclipse.lsp4j.DidChangeTextDocumentParams;
|
||||
import org.eclipse.lsp4j.DidCloseTextDocumentParams;
|
||||
import org.eclipse.lsp4j.DidOpenTextDocumentParams;
|
||||
import org.eclipse.lsp4j.Position;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.TextDocumentContentChangeEvent;
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.eclipse.lsp4j.TextDocumentItem;
|
||||
|
@ -78,49 +73,50 @@ public class DocumentEventManager {
|
|||
DidChangeTextDocumentParams changesParams = new DidChangeTextDocumentParams(new VersionedTextDocumentIdentifier(),
|
||||
Collections.singletonList(new TextDocumentContentChangeEvent()));
|
||||
changesParams.getTextDocument().setUri(identifier.getUri());
|
||||
|
||||
|
||||
changesParams.getTextDocument().setVersion(++version);
|
||||
|
||||
if (syncKind == TextDocumentSyncKind.Incremental) {
|
||||
TextDocumentContentChangeEvent changeEvent = changesParams.getContentChanges().get(0);
|
||||
CharSequence newText = event.getNewFragment();
|
||||
int offset = event.getOffset();
|
||||
int newTextLength = event.getNewLength();
|
||||
|
||||
EditorEventManager editorEventManager = EditorEventManagerBase.forUri(FileUtils.documentToUri(document));
|
||||
if (editorEventManager == null) {
|
||||
LOG.warn("no editor associated with document");
|
||||
return;
|
||||
}
|
||||
Editor editor = editorEventManager.editor;
|
||||
Position lspPosition = DocumentUtils.offsetToLSPPos(editor, offset);
|
||||
if (lspPosition == null) {
|
||||
return;
|
||||
}
|
||||
int startLine = lspPosition.getLine();
|
||||
int startColumn = lspPosition.getCharacter();
|
||||
CharSequence oldText = event.getOldFragment();
|
||||
|
||||
//if text was deleted/replaced, calculate the end position of inserted/deleted text
|
||||
int endLine, endColumn;
|
||||
if (oldText.length() > 0) {
|
||||
endLine = startLine + StringUtil.countNewLines(oldText);
|
||||
String content = oldText.toString();
|
||||
String[] oldLines = content.split("\n");
|
||||
int oldTextLength = oldLines.length == 0 ? 0 : oldLines[oldLines.length - 1].length();
|
||||
endColumn = content.endsWith("\n") ? 0 : oldLines.length == 1 ? startColumn + oldTextLength : oldTextLength;
|
||||
} else { //if insert or no text change, the end position is the same
|
||||
endLine = startLine;
|
||||
endColumn = startColumn;
|
||||
}
|
||||
Range range = new Range(new Position(startLine, startColumn), new Position(endLine, endColumn));
|
||||
changeEvent.setRange(range);
|
||||
changeEvent.setText(newText.toString());
|
||||
} else if (syncKind == TextDocumentSyncKind.Full) {
|
||||
// TODO this incremental update logic is kinda broken, investigate later...
|
||||
// if (syncKind == TextDocumentSyncKind.Incremental) {
|
||||
// TextDocumentContentChangeEvent changeEvent = changesParams.getContentChanges().get(0);
|
||||
// CharSequence newText = event.getNewFragment();
|
||||
// int offset = event.getOffset();
|
||||
// int newTextLength = event.getNewLength();
|
||||
//
|
||||
// EditorEventManager editorEventManager = EditorEventManagerBase.forUri(FileUtils.documentToUri(document));
|
||||
// if (editorEventManager == null) {
|
||||
// LOG.warn("no editor associated with document");
|
||||
// return;
|
||||
// }
|
||||
// Editor editor = editorEventManager.editor;
|
||||
// Position lspPosition = DocumentUtils.offsetToLSPPos(editor, offset);
|
||||
// if (lspPosition == null) {
|
||||
// return;
|
||||
// }
|
||||
// int startLine = lspPosition.getLine();
|
||||
// int startColumn = lspPosition.getCharacter();
|
||||
// CharSequence oldText = event.getOldFragment();
|
||||
//
|
||||
// //if text was deleted/replaced, calculate the end position of inserted/deleted text
|
||||
// int endLine, endColumn;
|
||||
// if (oldText.length() > 0) {
|
||||
// endLine = startLine + StringUtil.countNewLines(oldText);
|
||||
// String content = oldText.toString();
|
||||
// String[] oldLines = content.split("\n");
|
||||
// int oldTextLength = oldLines.length == 0 ? 0 : oldLines[oldLines.length - 1].length();
|
||||
// endColumn = content.endsWith("\n") ? 0 : oldLines.length == 1 ? startColumn + oldTextLength : oldTextLength;
|
||||
// } else { //if insert or no text change, the end position is the same
|
||||
// endLine = startLine;
|
||||
// endColumn = startColumn;
|
||||
// }
|
||||
// Range range = new Range(new Position(startLine, startColumn), new Position(endLine, endColumn));
|
||||
// changeEvent.setRange(range);
|
||||
// changeEvent.setText(newText.toString());
|
||||
// } else if (syncKind == TextDocumentSyncKind.Full) {
|
||||
if (syncKind != TextDocumentSyncKind.None) {
|
||||
changesParams.getContentChanges().get(0).setText(document.getText());
|
||||
}
|
||||
ApplicationUtils.pool(() -> wrapper.getRequestManager().didChange(changesParams));
|
||||
// }
|
||||
ApplicationUtil.pool(() -> wrapper.getRequestManager().didChange(changesParams));
|
||||
}
|
||||
|
||||
public void documentOpened() {
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package com.falsepattern.zigbrains.lsp.editor;
|
||||
|
||||
import com.falsepattern.zigbrains.common.util.ApplicationUtil;
|
||||
import com.falsepattern.zigbrains.common.util.FileUtil;
|
||||
import com.falsepattern.zigbrains.lsp.actions.LSPReferencesAction;
|
||||
import com.falsepattern.zigbrains.lsp.client.languageserver.ServerOptions;
|
||||
import com.falsepattern.zigbrains.lsp.client.languageserver.requestmanager.RequestManager;
|
||||
|
@ -25,11 +27,9 @@ import com.falsepattern.zigbrains.lsp.contributors.icon.LSPIconProvider;
|
|||
import com.falsepattern.zigbrains.lsp.contributors.psi.LSPPsiElement;
|
||||
import com.falsepattern.zigbrains.lsp.contributors.rename.LSPRenameProcessor;
|
||||
import com.falsepattern.zigbrains.lsp.listeners.LSPCaretListenerImpl;
|
||||
import com.falsepattern.zigbrains.lsp.requests.HoverHandler;
|
||||
import com.falsepattern.zigbrains.lsp.requests.Timeout;
|
||||
import com.falsepattern.zigbrains.lsp.requests.Timeouts;
|
||||
import com.falsepattern.zigbrains.lsp.requests.WorkspaceEditHandler;
|
||||
import com.falsepattern.zigbrains.lsp.utils.ApplicationUtils;
|
||||
import com.falsepattern.zigbrains.lsp.utils.DocumentUtils;
|
||||
import com.falsepattern.zigbrains.lsp.utils.FileUtils;
|
||||
import com.falsepattern.zigbrains.lsp.utils.GUIUtils;
|
||||
|
@ -54,15 +54,10 @@ import com.intellij.openapi.editor.EditorModificationUtil;
|
|||
import com.intellij.openapi.editor.LogicalPosition;
|
||||
import com.intellij.openapi.editor.ScrollType;
|
||||
import com.intellij.openapi.editor.SelectionModel;
|
||||
import com.intellij.openapi.editor.colors.EditorColors;
|
||||
import com.intellij.openapi.editor.event.DocumentEvent;
|
||||
import com.intellij.openapi.editor.event.DocumentListener;
|
||||
import com.intellij.openapi.editor.event.EditorMouseEvent;
|
||||
import com.intellij.openapi.editor.event.EditorMouseListener;
|
||||
import com.intellij.openapi.editor.event.EditorMouseMotionListener;
|
||||
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;
|
||||
import com.intellij.openapi.editor.markup.HighlighterLayer;
|
||||
import com.intellij.openapi.editor.markup.HighlighterTargetArea;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
|
||||
|
@ -72,7 +67,6 @@ import com.intellij.openapi.util.Pair;
|
|||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileManager;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
|
@ -96,8 +90,6 @@ import org.eclipse.lsp4j.DocumentFormattingParams;
|
|||
import org.eclipse.lsp4j.DocumentRangeFormattingParams;
|
||||
import org.eclipse.lsp4j.ExecuteCommandParams;
|
||||
import org.eclipse.lsp4j.FormattingOptions;
|
||||
import org.eclipse.lsp4j.Hover;
|
||||
import org.eclipse.lsp4j.HoverParams;
|
||||
import org.eclipse.lsp4j.InlayHint;
|
||||
import org.eclipse.lsp4j.InlayHintParams;
|
||||
import org.eclipse.lsp4j.InsertReplaceEdit;
|
||||
|
@ -124,7 +116,6 @@ import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
|||
import org.eclipse.lsp4j.jsonrpc.messages.Tuple;
|
||||
import org.eclipse.lsp4j.services.TextDocumentService;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import java.awt.Point;
|
||||
|
@ -144,15 +135,14 @@ import java.util.concurrent.ExecutionException;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.falsepattern.zigbrains.lsp.utils.ApplicationUtils.computableReadAction;
|
||||
import static com.falsepattern.zigbrains.lsp.utils.ApplicationUtils.computableWriteAction;
|
||||
import static com.falsepattern.zigbrains.lsp.utils.ApplicationUtils.invokeLater;
|
||||
import static com.falsepattern.zigbrains.lsp.utils.ApplicationUtils.pool;
|
||||
import static com.falsepattern.zigbrains.lsp.utils.ApplicationUtils.writeAction;
|
||||
import static com.falsepattern.zigbrains.common.util.ApplicationUtil.computableReadAction;
|
||||
import static com.falsepattern.zigbrains.common.util.ApplicationUtil.computableWriteAction;
|
||||
import static com.falsepattern.zigbrains.common.util.ApplicationUtil.invokeLater;
|
||||
import static com.falsepattern.zigbrains.common.util.ApplicationUtil.pool;
|
||||
import static com.falsepattern.zigbrains.common.util.ApplicationUtil.writeAction;
|
||||
import static com.falsepattern.zigbrains.lsp.utils.DocumentUtils.toEither;
|
||||
import static com.falsepattern.zigbrains.lsp.utils.GUIUtils.createAndShowEditorHint;
|
||||
|
||||
|
@ -370,8 +360,8 @@ public class EditorEventManager {
|
|||
res.forEach(l -> {
|
||||
Position start = l.getRange().getStart();
|
||||
Position end = l.getRange().getEnd();
|
||||
String uri = FileUtils.sanitizeURI(l.getUri());
|
||||
VirtualFile file = FileUtils.virtualFileFromURI(uri);
|
||||
String uri = FileUtil.sanitizeURI(l.getUri());
|
||||
VirtualFile file = FileUtil.virtualFileFromURI(uri);
|
||||
if (fast) {
|
||||
if (file == null)
|
||||
return;
|
||||
|
@ -1228,7 +1218,7 @@ public class EditorEventManager {
|
|||
}
|
||||
|
||||
public List<InlayHint> inlayHint() {
|
||||
var range = ApplicationUtils.computableReadAction(() -> {
|
||||
var range = ApplicationUtil.computableReadAction(() -> {
|
||||
var start = DocumentUtils.offsetToLSPPos(editor, 0);
|
||||
var end = DocumentUtils.offsetToLSPPos(editor, editor.getDocument().getTextLength());
|
||||
return new Range(start, end);
|
||||
|
@ -1295,14 +1285,16 @@ public class EditorEventManager {
|
|||
}
|
||||
}
|
||||
// Tries to go to definition
|
||||
public void gotoDefinition(PsiElement element) {
|
||||
public boolean gotoDefinition(PsiElement element) {
|
||||
if (editor.isDisposed()) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
val sourceOffset = element.getTextOffset();
|
||||
val loc = requestDefinition(DocumentUtils.offsetToLSPPos(editor, sourceOffset));
|
||||
if (loc == null)
|
||||
return false;
|
||||
|
||||
gotoLocation(loc);
|
||||
return gotoLocation(loc);
|
||||
}
|
||||
|
||||
// Tries to go to declaration / show usages based on the element which is
|
||||
|
@ -1322,7 +1314,7 @@ public class EditorEventManager {
|
|||
return;
|
||||
}
|
||||
|
||||
String locUri = FileUtils.sanitizeURI(loc.getUri());
|
||||
String locUri = FileUtil.sanitizeURI(loc.getUri());
|
||||
|
||||
if (identifier.getUri().equals(locUri)
|
||||
&& sourceOffset >= DocumentUtils.LSPPosToOffset(editor, loc.getRange().getStart())
|
||||
|
@ -1337,7 +1329,7 @@ public class EditorEventManager {
|
|||
}
|
||||
}
|
||||
|
||||
public void gotoLocation(Location loc) {
|
||||
public boolean gotoLocation(Location loc) {
|
||||
VirtualFile file = null;
|
||||
try {
|
||||
file = VfsUtil.findFileByURL(new URL(loc.getUri()));
|
||||
|
@ -1359,9 +1351,11 @@ public class EditorEventManager {
|
|||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
} else {
|
||||
LOG.warn("Empty file for " + loc.getUri());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void requestAndShowCodeActions() {
|
||||
|
|
|
@ -15,15 +15,12 @@
|
|||
*/
|
||||
package com.falsepattern.zigbrains.lsp.editor;
|
||||
|
||||
import com.falsepattern.zigbrains.lsp.utils.ApplicationUtils;
|
||||
import com.falsepattern.zigbrains.common.util.ApplicationUtil;
|
||||
import com.falsepattern.zigbrains.lsp.utils.FileUtils;
|
||||
import com.falsepattern.zigbrains.lsp.utils.OSUtils;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import lombok.val;
|
||||
import org.eclipse.lsp4j.Diagnostic;
|
||||
|
||||
import java.awt.KeyboardFocusManager;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
@ -86,7 +83,7 @@ public class EditorEventManagerBase {
|
|||
if (runOnRegistry.containsKey(manager.editor)) {
|
||||
var tasks = runOnRegistry.remove(manager.editor);
|
||||
for (var task: tasks) {
|
||||
ApplicationUtils.invokeLater(task);
|
||||
ApplicationUtil.invokeLater(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -97,7 +94,7 @@ public class EditorEventManagerBase {
|
|||
var manager = forEditor(editor);
|
||||
if (manager != null) {
|
||||
for (var task: runnables) {
|
||||
ApplicationUtils.invokeLater(task);
|
||||
ApplicationUtil.invokeLater(task);
|
||||
}
|
||||
} else {
|
||||
runOnRegistry.computeIfAbsent(editor, (ignored) -> new ArrayList<>()).addAll(List.of(runnables));
|
||||
|
|
|
@ -15,11 +15,12 @@
|
|||
*/
|
||||
package com.falsepattern.zigbrains.lsp.listeners;
|
||||
|
||||
import com.falsepattern.zigbrains.common.util.ApplicationUtil;
|
||||
import com.falsepattern.zigbrains.common.util.FileUtil;
|
||||
import com.falsepattern.zigbrains.lsp.IntellijLanguageClient;
|
||||
import com.falsepattern.zigbrains.lsp.client.languageserver.ServerStatus;
|
||||
import com.falsepattern.zigbrains.lsp.client.languageserver.wrapper.LanguageServerWrapper;
|
||||
import com.falsepattern.zigbrains.lsp.editor.EditorEventManagerBase;
|
||||
import com.falsepattern.zigbrains.lsp.utils.ApplicationUtils;
|
||||
import com.falsepattern.zigbrains.lsp.utils.FileUtils;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
|
@ -27,7 +28,7 @@ import com.intellij.openapi.fileEditor.FileDocumentManager;
|
|||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileMoveEvent;
|
||||
import com.intellij.openapi.vfs.newvfs.events.VFileMoveEvent;
|
||||
import org.eclipse.lsp4j.DidChangeWatchedFilesParams;
|
||||
import org.eclipse.lsp4j.FileChangeType;
|
||||
import org.eclipse.lsp4j.FileEvent;
|
||||
|
@ -51,7 +52,7 @@ class LSPFileEventManager {
|
|||
* @param doc The document
|
||||
*/
|
||||
static void willSave(Document doc) {
|
||||
String uri = FileUtils.VFSToURI(FileDocumentManager.getInstance().getFile(doc));
|
||||
String uri = FileUtil.URIFromVirtualFile(FileDocumentManager.getInstance().getFile(doc));
|
||||
EditorEventManagerBase.willSave(uri);
|
||||
}
|
||||
|
||||
|
@ -72,16 +73,16 @@ class LSPFileEventManager {
|
|||
if (!FileUtils.isFileSupported(file)) {
|
||||
return;
|
||||
}
|
||||
String uri = FileUtils.VFSToURI(file);
|
||||
String uri = FileUtil.URIFromVirtualFile(file);
|
||||
if (uri == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ApplicationUtils.invokeAfterPsiEvents(() -> {
|
||||
ApplicationUtil.invokeAfterPsiEvents(() -> {
|
||||
EditorEventManagerBase.documentSaved(uri);
|
||||
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(uri,
|
||||
FileUtils.projectToUri(p), FileChangeType.Changed));
|
||||
});
|
||||
FileUtils.findProjectsFor(file)
|
||||
.forEach(p -> changedConfiguration(uri, FileUtils.projectToUri(p), FileChangeType.Changed));
|
||||
}, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -89,19 +90,19 @@ class LSPFileEventManager {
|
|||
*
|
||||
* @param event The file move event
|
||||
*/
|
||||
static void fileMoved(VirtualFileMoveEvent event) {
|
||||
static void fileMoved(VFileMoveEvent event) {
|
||||
try {
|
||||
VirtualFile file = event.getFile();
|
||||
if (!FileUtils.isFileSupported(file)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String newFileUri = FileUtils.VFSToURI(file);
|
||||
String oldParentUri = FileUtils.VFSToURI(event.getOldParent());
|
||||
String newFileUri = FileUtil.URIFromVirtualFile(file);
|
||||
String oldParentUri = FileUtil.URIFromVirtualFile(event.getOldParent());
|
||||
if (newFileUri == null || oldParentUri == null) {
|
||||
return;
|
||||
}
|
||||
String oldFileUri = String.format("%s/%s", oldParentUri, event.getFileName());
|
||||
String oldFileUri = String.format("%s/%s", oldParentUri, event.getFile().getName());
|
||||
closeAndReopenAffectedFile(file, oldFileUri);
|
||||
} catch (Exception e) {
|
||||
LOG.warn("LSP file move event failed due to :", e);
|
||||
|
@ -117,14 +118,14 @@ class LSPFileEventManager {
|
|||
if (!FileUtils.isFileSupported(file)) {
|
||||
return;
|
||||
}
|
||||
String uri = FileUtils.VFSToURI(file);
|
||||
String uri = FileUtil.URIFromVirtualFile(file);
|
||||
if (uri == null) {
|
||||
return;
|
||||
}
|
||||
ApplicationUtils.invokeAfterPsiEvents(() -> {
|
||||
ApplicationUtil.invokeAfterPsiEvents(() -> {
|
||||
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(uri,
|
||||
FileUtils.projectToUri(p), FileChangeType.Deleted));
|
||||
});
|
||||
}, true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -134,7 +135,7 @@ class LSPFileEventManager {
|
|||
* @param newFileName the new file name
|
||||
*/
|
||||
static void fileRenamed(String oldFileName, String newFileName) {
|
||||
ApplicationUtils.invokeAfterPsiEvents(() -> {
|
||||
ApplicationUtil.invokeAfterPsiEvents(() -> {
|
||||
try {
|
||||
// Getting the right file is not trivial here since we only have the file name. Since we have to iterate over
|
||||
// all opened projects and filter based on the file name.
|
||||
|
@ -142,22 +143,24 @@ class LSPFileEventManager {
|
|||
.flatMap(p -> searchFiles(newFileName, p).stream())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
for (VirtualFile file : files) {
|
||||
if (!FileUtils.isFileSupported(file)) {
|
||||
continue;
|
||||
ApplicationUtil.invokeLater(() -> {
|
||||
for (VirtualFile file : files) {
|
||||
if (!FileUtils.isFileSupported(file)) {
|
||||
continue;
|
||||
}
|
||||
String newFileUri = FileUtil.URIFromVirtualFile(file);
|
||||
String oldFileUri = newFileUri.replace(file.getName(), oldFileName);
|
||||
closeAndReopenAffectedFile(file, oldFileUri);
|
||||
}
|
||||
String newFileUri = FileUtils.VFSToURI(file);
|
||||
String oldFileUri = newFileUri.replace(file.getName(), oldFileName);
|
||||
closeAndReopenAffectedFile(file, oldFileUri);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
LOG.warn("LSP file rename event failed due to : ", e);
|
||||
}
|
||||
});
|
||||
}, true, false);
|
||||
}
|
||||
|
||||
private static void closeAndReopenAffectedFile(VirtualFile file, String oldFileUri) {
|
||||
String newFileUri = FileUtils.VFSToURI(file);
|
||||
String newFileUri = FileUtil.URIFromVirtualFile(file);
|
||||
|
||||
// Notifies the language server.
|
||||
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(oldFileUri,
|
||||
|
@ -172,7 +175,7 @@ class LSPFileEventManager {
|
|||
if (!newFileUri.equals(oldFileUri)) {
|
||||
// Re-open file to so that the new editor will be connected to the language server.
|
||||
FileEditorManager fileEditorManager = FileEditorManager.getInstance(p);
|
||||
ApplicationUtils.invokeLater(() -> {
|
||||
ApplicationUtil.invokeLater(() -> {
|
||||
fileEditorManager.closeFile(file);
|
||||
fileEditorManager.openFile(file, true);
|
||||
});
|
||||
|
@ -189,17 +192,17 @@ class LSPFileEventManager {
|
|||
if (!FileUtils.isFileSupported(file)) {
|
||||
return;
|
||||
}
|
||||
String uri = FileUtils.VFSToURI(file);
|
||||
String uri = FileUtil.URIFromVirtualFile(file);
|
||||
if (uri != null) {
|
||||
ApplicationUtils.invokeAfterPsiEvents(() -> {
|
||||
ApplicationUtil.invokeAfterPsiEvents(() -> {
|
||||
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(uri,
|
||||
FileUtils.projectToUri(p), FileChangeType.Created));
|
||||
});
|
||||
}, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static void changedConfiguration(String uri, String projectUri, FileChangeType typ) {
|
||||
ApplicationUtils.pool(() -> {
|
||||
ApplicationUtil.pool(() -> {
|
||||
DidChangeWatchedFilesParams params = getDidChangeWatchedFilesParams(uri, typ);
|
||||
Set<LanguageServerWrapper> wrappers = IntellijLanguageClient.getAllServerWrappersFor(projectUri);
|
||||
if (wrappers == null) {
|
||||
|
|
|
@ -16,14 +16,42 @@
|
|||
package com.falsepattern.zigbrains.lsp.listeners;
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileCopyEvent;
|
||||
import com.intellij.openapi.vfs.VirtualFileEvent;
|
||||
import com.intellij.openapi.vfs.VirtualFileListener;
|
||||
import com.intellij.openapi.vfs.VirtualFileMoveEvent;
|
||||
import com.intellij.openapi.vfs.VirtualFilePropertyEvent;
|
||||
import com.intellij.openapi.vfs.newvfs.BulkFileListener;
|
||||
import com.intellij.openapi.vfs.newvfs.events.VFileContentChangeEvent;
|
||||
import com.intellij.openapi.vfs.newvfs.events.VFileCopyEvent;
|
||||
import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent;
|
||||
import com.intellij.openapi.vfs.newvfs.events.VFileDeleteEvent;
|
||||
import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
|
||||
import com.intellij.openapi.vfs.newvfs.events.VFileMoveEvent;
|
||||
import com.intellij.openapi.vfs.newvfs.events.VFilePropertyChangeEvent;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class VFSListener implements VirtualFileListener {
|
||||
import java.util.List;
|
||||
|
||||
public class VFSListener implements BulkFileListener {
|
||||
|
||||
@Override
|
||||
public void before(@NotNull List<? extends @NotNull VFileEvent> events) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void after(@NotNull List<? extends @NotNull VFileEvent> events) {
|
||||
for (val event: events) {
|
||||
if (event instanceof VFilePropertyChangeEvent propEvent)
|
||||
propertyChanged(propEvent);
|
||||
else if (event instanceof VFileContentChangeEvent changeEvent)
|
||||
contentsChanged(changeEvent);
|
||||
else if (event instanceof VFileDeleteEvent deleteEvent)
|
||||
fileDeleted(deleteEvent);
|
||||
else if (event instanceof VFileMoveEvent moveEvent)
|
||||
fileMoved(moveEvent);
|
||||
else if (event instanceof VFileCopyEvent copyEvent)
|
||||
fileCopied(copyEvent);
|
||||
else if (event instanceof VFileCreateEvent createEvent)
|
||||
fileCreated(createEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired when a virtual file is renamed from within IDEA, or its writable status is changed.
|
||||
|
@ -31,8 +59,7 @@ public class VFSListener implements VirtualFileListener {
|
|||
*
|
||||
* @param event the event object containing information about the change.
|
||||
*/
|
||||
@Override
|
||||
public void propertyChanged(@NotNull VirtualFilePropertyEvent event) {
|
||||
public void propertyChanged(@NotNull VFilePropertyChangeEvent event) {
|
||||
if (event.getPropertyName().equals(VirtualFile.PROP_NAME)) {
|
||||
LSPFileEventManager.fileRenamed((String) event.getOldValue(), (String) event.getNewValue());
|
||||
}
|
||||
|
@ -43,8 +70,7 @@ public class VFSListener implements VirtualFileListener {
|
|||
*
|
||||
* @param event the event object containing information about the change.
|
||||
*/
|
||||
@Override
|
||||
public void contentsChanged(@NotNull VirtualFileEvent event) {
|
||||
public void contentsChanged(@NotNull VFileContentChangeEvent event) {
|
||||
LSPFileEventManager.fileChanged(event.getFile());
|
||||
}
|
||||
|
||||
|
@ -53,8 +79,7 @@ public class VFSListener implements VirtualFileListener {
|
|||
*
|
||||
* @param event the event object containing information about the change.
|
||||
*/
|
||||
@Override
|
||||
public void fileDeleted(@NotNull VirtualFileEvent event) {
|
||||
public void fileDeleted(@NotNull VFileDeleteEvent event) {
|
||||
LSPFileEventManager.fileDeleted(event.getFile());
|
||||
}
|
||||
|
||||
|
@ -63,8 +88,7 @@ public class VFSListener implements VirtualFileListener {
|
|||
*
|
||||
* @param event the event object containing information about the change.
|
||||
*/
|
||||
@Override
|
||||
public void fileMoved(@NotNull VirtualFileMoveEvent event) {
|
||||
public void fileMoved(@NotNull VFileMoveEvent event) {
|
||||
LSPFileEventManager.fileMoved(event);
|
||||
}
|
||||
|
||||
|
@ -73,9 +97,8 @@ public class VFSListener implements VirtualFileListener {
|
|||
*
|
||||
* @param event the event object containing information about the change.
|
||||
*/
|
||||
@Override
|
||||
public void fileCopied(@NotNull VirtualFileCopyEvent event) {
|
||||
fileCreated(event);
|
||||
public void fileCopied(@NotNull VFileCopyEvent event) {
|
||||
LSPFileEventManager.fileCreated(event.findCreatedFile());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -83,44 +106,7 @@ public class VFSListener implements VirtualFileListener {
|
|||
*
|
||||
* @param event the event object containing information about the change.
|
||||
*/
|
||||
@Override
|
||||
public void fileCreated(@NotNull VirtualFileEvent event) {
|
||||
public void fileCreated(@NotNull VFileCreateEvent event) {
|
||||
LSPFileEventManager.fileCreated(event.getFile());
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired before the change of a name or writable status of a file is processed.
|
||||
*
|
||||
* @param event the event object containing information about the change.
|
||||
*/
|
||||
@Override
|
||||
public void beforePropertyChange(@NotNull VirtualFilePropertyEvent event) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired before the change of contents of a file is processed.
|
||||
*
|
||||
* @param event the event object containing information about the change.
|
||||
*/
|
||||
@Override
|
||||
public void beforeContentsChange(@NotNull VirtualFileEvent event) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired before the deletion of a file is processed.
|
||||
*
|
||||
* @param event the event object containing information about the change.
|
||||
*/
|
||||
@Override
|
||||
public void beforeFileDeletion(@NotNull VirtualFileEvent event) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired before the movement of a file is processed.
|
||||
*
|
||||
* @param event the event object containing information about the change.
|
||||
*/
|
||||
@Override
|
||||
public void beforeFileMovement(@NotNull VirtualFileMoveEvent event) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,9 +16,6 @@
|
|||
package com.falsepattern.zigbrains.lsp.requests;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import com.vladsch.flexmark.html.HtmlRenderer;
|
||||
import com.vladsch.flexmark.parser.Parser;
|
||||
import org.eclipse.lsp4j.Hover;
|
||||
import org.eclipse.lsp4j.MarkedString;
|
||||
import org.eclipse.lsp4j.MarkupContent;
|
||||
|
@ -60,13 +57,9 @@ public class HoverHandler {
|
|||
"```" + markedString.getLanguage() + " " + markedString.getValue() + "```" :
|
||||
"";
|
||||
}
|
||||
Parser parser = Parser.builder().build();
|
||||
HtmlRenderer renderer = HtmlRenderer.builder().build();
|
||||
if (!string.isEmpty()) {
|
||||
result.add(renderer.render(parser.parse(string)));
|
||||
}
|
||||
result.add(string);
|
||||
}
|
||||
return "<html>" + String.join("\n\n", result) + "</html>";
|
||||
return String.join("\n", result);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
|
@ -75,9 +68,7 @@ public class HoverHandler {
|
|||
if (markedContent.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
Parser parser = Parser.builder().build();
|
||||
HtmlRenderer renderer = HtmlRenderer.builder().build();
|
||||
return "<html>" + renderer.render(parser.parse(markedContent)) + "</html>";
|
||||
return markedContent;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
|
|
|
@ -15,10 +15,11 @@
|
|||
*/
|
||||
package com.falsepattern.zigbrains.lsp.requests;
|
||||
|
||||
import com.falsepattern.zigbrains.common.util.ApplicationUtil;
|
||||
import com.falsepattern.zigbrains.common.util.FileUtil;
|
||||
import com.falsepattern.zigbrains.lsp.contributors.psi.LSPPsiElement;
|
||||
import com.falsepattern.zigbrains.lsp.editor.EditorEventManager;
|
||||
import com.falsepattern.zigbrains.lsp.editor.EditorEventManagerBase;
|
||||
import com.falsepattern.zigbrains.lsp.utils.ApplicationUtils;
|
||||
import com.falsepattern.zigbrains.lsp.utils.DocumentUtils;
|
||||
import com.falsepattern.zigbrains.lsp.utils.FileUtils;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
|
@ -57,8 +58,8 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.falsepattern.zigbrains.lsp.utils.ApplicationUtils.invokeLater;
|
||||
import static com.falsepattern.zigbrains.lsp.utils.ApplicationUtils.writeAction;
|
||||
import static com.falsepattern.zigbrains.common.util.ApplicationUtil.invokeLater;
|
||||
import static com.falsepattern.zigbrains.common.util.ApplicationUtil.writeAction;
|
||||
import static com.falsepattern.zigbrains.lsp.utils.DocumentUtils.toEither;
|
||||
|
||||
/**
|
||||
|
@ -81,9 +82,9 @@ public class WorkspaceEditHandler {
|
|||
TextEdit edit = new TextEdit(lspRange, newName);
|
||||
String uri = null;
|
||||
try {
|
||||
uri = FileUtils.sanitizeURI(
|
||||
new URL(ui.getVirtualFile().getUrl().replace(" ", FileUtils.SPACE_ENCODED)).toURI()
|
||||
.toString());
|
||||
uri = FileUtil.sanitizeURI(
|
||||
new URL(ui.getVirtualFile().getUrl().replace(" ", FileUtil.SPACE_ENCODED)).toURI()
|
||||
.toString());
|
||||
} catch (MalformedURLException | URISyntaxException e) {
|
||||
LOG.warn(e);
|
||||
}
|
||||
|
@ -131,7 +132,7 @@ public class WorkspaceEditHandler {
|
|||
TextDocumentEdit textEdit = tEdit.getLeft();
|
||||
VersionedTextDocumentIdentifier doc = textEdit.getTextDocument();
|
||||
int version = doc.getVersion() != null ? doc.getVersion() : Integer.MAX_VALUE;
|
||||
String uri = FileUtils.sanitizeURI(doc.getUri());
|
||||
String uri = FileUtil.sanitizeURI(doc.getUri());
|
||||
EditorEventManager manager = EditorEventManagerBase.forUri(uri);
|
||||
if (manager != null) {
|
||||
curProject[0] = manager.editor.getProject();
|
||||
|
@ -151,7 +152,7 @@ public class WorkspaceEditHandler {
|
|||
|
||||
} else if (changes != null) {
|
||||
changes.forEach((key, lChanges) -> {
|
||||
String uri = FileUtils.sanitizeURI(key);
|
||||
String uri = FileUtil.sanitizeURI(key);
|
||||
|
||||
EditorEventManager manager = EditorEventManagerBase.forUri(uri);
|
||||
if (manager != null) {
|
||||
|
@ -198,18 +199,18 @@ public class WorkspaceEditHandler {
|
|||
Project[] projects = ProjectManager.getInstance().getOpenProjects();
|
||||
//Infer the project from the uri
|
||||
Project project = Stream.of(projects)
|
||||
.map(p -> new ImmutablePair<>(FileUtils.VFSToURI(ProjectUtil.guessProjectDir(p)), p))
|
||||
.map(p -> new ImmutablePair<>(FileUtil.URIFromVirtualFile(ProjectUtil.guessProjectDir(p)), p))
|
||||
.filter(p -> uri.startsWith(p.getLeft())).sorted(Collections.reverseOrder())
|
||||
.map(ImmutablePair::getRight).findFirst().orElse(projects[0]);
|
||||
VirtualFile file = null;
|
||||
try {
|
||||
file = LocalFileSystem.getInstance().findFileByIoFile(new File(new URI(FileUtils.sanitizeURI(uri))));
|
||||
file = LocalFileSystem.getInstance().findFileByIoFile(new File(new URI(FileUtil.sanitizeURI(uri))));
|
||||
} catch (URISyntaxException e) {
|
||||
LOG.warn(e);
|
||||
}
|
||||
FileEditorManager fileEditorManager = FileEditorManager.getInstance(project);
|
||||
OpenFileDescriptor descriptor = new OpenFileDescriptor(project, file);
|
||||
Editor editor = ApplicationUtils
|
||||
Editor editor = ApplicationUtil
|
||||
.computableWriteAction(() -> fileEditorManager.openTextEditor(descriptor, false));
|
||||
openedEditors.add(file);
|
||||
curProject[0] = editor.getProject();
|
||||
|
|
|
@ -31,7 +31,7 @@ import org.jetbrains.annotations.Nullable;
|
|||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.falsepattern.zigbrains.lsp.utils.ApplicationUtils.computableReadAction;
|
||||
import static com.falsepattern.zigbrains.common.util.ApplicationUtil.computableReadAction;
|
||||
import static java.lang.Math.max;
|
||||
import static java.lang.Math.min;
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package com.falsepattern.zigbrains.lsp.utils;
|
||||
|
||||
import com.falsepattern.zigbrains.common.util.FileUtil;
|
||||
import com.falsepattern.zigbrains.lsp.IntellijLanguageClient;
|
||||
import com.falsepattern.zigbrains.lsp.extensions.LSPExtensionManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
|
@ -27,21 +28,19 @@ import com.intellij.openapi.fileEditor.TextEditor;
|
|||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.project.ProjectUtil;
|
||||
import com.intellij.openapi.util.io.FileUtilRt;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.search.FilenameIndex;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.testFramework.LightVirtualFileBase;
|
||||
import lombok.val;
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
@ -50,19 +49,12 @@ import java.util.List;
|
|||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.falsepattern.zigbrains.lsp.utils.ApplicationUtils.computableReadAction;
|
||||
import static com.falsepattern.zigbrains.common.util.ApplicationUtil.computableReadAction;
|
||||
|
||||
/**
|
||||
* Various file / uri related methods
|
||||
*/
|
||||
public class FileUtils {
|
||||
private final static OS os = (System.getProperty("os.name").toLowerCase().contains("win")) ? OS.WINDOWS : OS.UNIX;
|
||||
private final static String COLON_ENCODED = "%3A";
|
||||
public final static String SPACE_ENCODED = "%20";
|
||||
private final static String URI_FILE_BEGIN = "file:";
|
||||
private final static String URI_VALID_FILE_BEGIN = "file:///";
|
||||
private final static char URI_PATH_SEP = '/';
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(FileUtils.class);
|
||||
|
||||
public static List<Editor> getAllOpenedEditors(Project project) {
|
||||
|
@ -83,7 +75,7 @@ public class FileUtils {
|
|||
}
|
||||
|
||||
public static List<Editor> getAllOpenedEditorsForUri(@NotNull Project project, String uri) {
|
||||
VirtualFile file = virtualFileFromURI(uri);
|
||||
VirtualFile file = FileUtil.virtualFileFromURI(uri);
|
||||
if (file == null)
|
||||
return Collections.emptyList();
|
||||
return getAllOpenedEditorsForVirtualFile(project, file);
|
||||
|
@ -106,38 +98,13 @@ public class FileUtils {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This can be used to instantly apply a language server definition without restarting the IDE.
|
||||
*/
|
||||
public static void reloadAllEditors() {
|
||||
Project[] openProjects = ProjectManager.getInstance().getOpenProjects();
|
||||
for (Project project : openProjects) {
|
||||
reloadEditors(project);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This can be used to instantly apply a project-specific language server definition without restarting the
|
||||
* project/IDE.
|
||||
*
|
||||
* @param project The project instance which need to be restarted
|
||||
*/
|
||||
public static void reloadEditors(@NotNull Project project) {
|
||||
try {
|
||||
List<Editor> allOpenedEditors = FileUtils.getAllOpenedEditors(project);
|
||||
allOpenedEditors.forEach(IntellijLanguageClient::editorClosed);
|
||||
allOpenedEditors.forEach(IntellijLanguageClient::editorOpened);
|
||||
} catch (Exception e) {
|
||||
LOG.warn(String.format("Refreshing project: %s is failed due to: ", project.getName()), e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Editor editorFromPsiFile(PsiFile psiFile) {
|
||||
return editorFromVirtualFile(psiFile.getVirtualFile(), psiFile.getProject());
|
||||
}
|
||||
|
||||
public static Editor editorFromUri(String uri, Project project) {
|
||||
return editorFromVirtualFile(virtualFileFromURI(uri), project);
|
||||
return editorFromVirtualFile(FileUtil.virtualFileFromURI(uri), project);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
@ -149,15 +116,6 @@ public class FileUtils {
|
|||
return null;
|
||||
}
|
||||
|
||||
public static VirtualFile virtualFileFromURI(String uri) {
|
||||
try {
|
||||
return LocalFileSystem.getInstance().findFileByIoFile(new File(new URI(sanitizeURI(uri))));
|
||||
} catch (URISyntaxException e) {
|
||||
LOG.warn(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a file type given an editor
|
||||
*
|
||||
|
@ -185,79 +143,14 @@ public class FileUtils {
|
|||
* @return The URI
|
||||
*/
|
||||
public static String editorToURIString(Editor editor) {
|
||||
return sanitizeURI(VFSToURI(FileDocumentManager.getInstance().getFile(editor.getDocument())));
|
||||
return FileUtil.sanitizeURI(
|
||||
FileUtil.URIFromVirtualFile(FileDocumentManager.getInstance().getFile(editor.getDocument())));
|
||||
}
|
||||
|
||||
public static VirtualFile virtualFileFromEditor(Editor editor) {
|
||||
return FileDocumentManager.getInstance().getFile(editor.getDocument());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URI string corresponding to a VirtualFileSystem file
|
||||
*
|
||||
* @param file The file
|
||||
* @return the URI
|
||||
*/
|
||||
public static String VFSToURI(VirtualFile file) {
|
||||
return file == null? null : pathToUri(file.getPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes common problems in uri, mainly related to Windows
|
||||
*
|
||||
* @param uri The uri to sanitize
|
||||
* @return The sanitized uri
|
||||
*/
|
||||
public static String sanitizeURI(String uri) {
|
||||
if (uri != null) {
|
||||
StringBuilder reconstructed = new StringBuilder();
|
||||
String uriCp = uri.replaceAll(" ", SPACE_ENCODED); //Don't trust servers
|
||||
if (!uri.startsWith(URI_FILE_BEGIN)) {
|
||||
LOG.warn("Malformed uri : " + uri);
|
||||
return uri; //Probably not an uri
|
||||
} else {
|
||||
uriCp = uriCp.substring(URI_FILE_BEGIN.length());
|
||||
while (uriCp.startsWith(Character.toString(URI_PATH_SEP))) {
|
||||
uriCp = uriCp.substring(1);
|
||||
}
|
||||
reconstructed.append(URI_VALID_FILE_BEGIN);
|
||||
if (os == OS.UNIX) {
|
||||
return reconstructed.append(uriCp).toString();
|
||||
} else {
|
||||
reconstructed.append(uriCp.substring(0, uriCp.indexOf(URI_PATH_SEP)));
|
||||
char driveLetter = reconstructed.charAt(URI_VALID_FILE_BEGIN.length());
|
||||
if (Character.isLowerCase(driveLetter)) {
|
||||
reconstructed.setCharAt(URI_VALID_FILE_BEGIN.length(), Character.toUpperCase(driveLetter));
|
||||
}
|
||||
if (reconstructed.toString().endsWith(COLON_ENCODED)) {
|
||||
reconstructed.delete(reconstructed.length() - 3, reconstructed.length());
|
||||
}
|
||||
if (!reconstructed.toString().endsWith(":")) {
|
||||
reconstructed.append(":");
|
||||
}
|
||||
return reconstructed.append(uriCp.substring(uriCp.indexOf(URI_PATH_SEP))).toString();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms an URI string into a VFS file
|
||||
*
|
||||
* @param uri The uri
|
||||
* @return The virtual file
|
||||
*/
|
||||
public static VirtualFile URIToVFS(String uri) {
|
||||
try {
|
||||
return LocalFileSystem.getInstance().findFileByIoFile(new File(new URI(sanitizeURI(uri))));
|
||||
} catch (URISyntaxException e) {
|
||||
LOG.warn(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the project base dir uri given an editor
|
||||
*
|
||||
|
@ -265,42 +158,82 @@ public class FileUtils {
|
|||
* @return The project whose the editor belongs
|
||||
*/
|
||||
public static String editorToProjectFolderUri(Editor editor) {
|
||||
return pathToUri(editorToProjectFolderPath(editor));
|
||||
return FileUtil.pathToUri(editorToProjectFolderPath(editor));
|
||||
}
|
||||
|
||||
public static String editorToProjectFolderPath(Editor editor) {
|
||||
if (editor != null && editor.getProject() != null && editor.getProject().getBasePath() != null) {
|
||||
return new File(editor.getProject().getBasePath()).getAbsolutePath();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (editor == null)
|
||||
return null;
|
||||
|
||||
/**
|
||||
* Transforms a path into an URI string
|
||||
*
|
||||
* @param path The path
|
||||
* @return The uri
|
||||
*/
|
||||
public static String pathToUri(@Nullable String path) {
|
||||
return path != null ? sanitizeURI(new File(path).toURI().toString()) : null;
|
||||
val project = editor.getProject();
|
||||
if (project == null)
|
||||
return null;
|
||||
|
||||
val projectDir = ProjectUtil.guessProjectDir(editor.getProject());
|
||||
if (projectDir == null)
|
||||
return null;
|
||||
|
||||
return projectDir.toNioPath().toAbsolutePath().toString();
|
||||
}
|
||||
|
||||
public static String projectToUri(Project project) {
|
||||
if (project != null && project.getBasePath() != null) {
|
||||
return pathToUri(new File(project.getBasePath()).getAbsolutePath());
|
||||
}
|
||||
return null;
|
||||
if (project == null)
|
||||
return null;
|
||||
|
||||
val path = ProjectUtil.guessProjectDir(project);
|
||||
if (path == null)
|
||||
return null;
|
||||
|
||||
return FileUtil.pathToUri(path.toNioPath());
|
||||
}
|
||||
|
||||
public static String documentToUri(Document document) {
|
||||
return sanitizeURI(VFSToURI(FileDocumentManager.getInstance().getFile(document)));
|
||||
return FileUtil.sanitizeURI(FileUtil.URIFromVirtualFile(FileDocumentManager.getInstance().getFile(document)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Object representing the OS type (Windows or Unix)
|
||||
* Find projects which contains the given file. This search runs among all open projects.
|
||||
*/
|
||||
public enum OS {
|
||||
WINDOWS, UNIX
|
||||
@NotNull
|
||||
public static Set<Project> findProjectsFor(@NotNull VirtualFile file) {
|
||||
return Arrays.stream(ProjectManager.getInstance().getOpenProjects())
|
||||
.filter(p -> searchFiles(file.getName(), p).stream().anyMatch(f -> f.getPath().equals(file.getPath())))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static Collection<VirtualFile> searchFiles(String fileName, Project p) {
|
||||
try {
|
||||
return computableReadAction(() -> FilenameIndex.getVirtualFilesByName(fileName, GlobalSearchScope.projectScope(p)));
|
||||
} catch (Throwable t) {
|
||||
// Todo - Find a proper way to handle when IDEA file indexing is in-progress.
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This can be used to instantly apply a language server definition without restarting the IDE.
|
||||
*/
|
||||
public static void reloadAllEditors() {
|
||||
Project[] openProjects = ProjectManager.getInstance().getOpenProjects();
|
||||
for (Project project : openProjects) {
|
||||
reloadEditors(project);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This can be used to instantly apply a project-specific language server definition without restarting the
|
||||
* project/IDE.
|
||||
*
|
||||
* @param project The project instance which need to be restarted
|
||||
*/
|
||||
public static void reloadEditors(@NotNull Project project) {
|
||||
try {
|
||||
List<Editor> allOpenedEditors = FileUtils.getAllOpenedEditors(project);
|
||||
allOpenedEditors.forEach(IntellijLanguageClient::editorClosed);
|
||||
allOpenedEditors.forEach(IntellijLanguageClient::editorOpened);
|
||||
} catch (Exception e) {
|
||||
LOG.warn(String.format("Refreshing project: %s is failed due to: ", project.getName()), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -322,25 +255,6 @@ public class FileUtils {
|
|||
return IntellijLanguageClient.isExtensionSupported(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find projects which contains the given file. This search runs among all open projects.
|
||||
*/
|
||||
@NotNull
|
||||
public static Set<Project> findProjectsFor(@NotNull VirtualFile file) {
|
||||
return Arrays.stream(ProjectManager.getInstance().getOpenProjects())
|
||||
.filter(p -> searchFiles(file.getName(), p).stream().anyMatch(f -> f.getPath().equals(file.getPath())))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static Collection<VirtualFile> searchFiles(String fileName, Project p) {
|
||||
try {
|
||||
return computableReadAction(() -> FilenameIndex.getVirtualFilesByName(fileName, GlobalSearchScope.projectScope(p)));
|
||||
} catch (Throwable t) {
|
||||
// Todo - Find a proper way to handle when IDEA file indexing is in-progress.
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the file in editor is supported by this LS client library.
|
||||
*/
|
||||
|
|
|
@ -46,7 +46,7 @@ import java.net.URISyntaxException;
|
|||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import static com.falsepattern.zigbrains.lsp.utils.ApplicationUtils.writeAction;
|
||||
import static com.falsepattern.zigbrains.common.util.ApplicationUtil.writeAction;
|
||||
|
||||
public final class GUIUtils {
|
||||
private static final LSPDefaultIconProvider DEFAULT_ICON_PROVIDER = new LSPDefaultIconProvider();
|
||||
|
|
|
@ -1,17 +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.
|
||||
-->
|
||||
|
||||
<idea-plugin/>
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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.console;
|
||||
|
||||
import com.intellij.execution.filters.ConsoleFilterProvider;
|
||||
import com.intellij.execution.filters.Filter;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ZigConsoleFilterProvider implements ConsoleFilterProvider {
|
||||
@Override
|
||||
public Filter @NotNull [] getDefaultFilters(@NotNull Project project) {
|
||||
return new Filter[]{new ZigSourceFileFilter(project)};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* 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.console;
|
||||
|
||||
import com.falsepattern.zigbrains.common.util.FileUtil;
|
||||
import com.intellij.execution.filters.Filter;
|
||||
import com.intellij.execution.filters.OpenFileHyperlinkInfo;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
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.Optional;
|
||||
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, Path 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)) && projectPath != null) {
|
||||
path = projectPath.resolve(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();
|
||||
val projectPath = Optional.ofNullable(ProjectUtil.guessProjectDir(project)).map(VirtualFile::toNioPath).orElse(null);
|
||||
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)));
|
||||
|
||||
}
|
||||
return new Result(results);
|
||||
}
|
||||
}
|
|
@ -18,13 +18,13 @@ package com.falsepattern.zigbrains.project.execution;
|
|||
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.intellij.execution.process.CapturingProcessHandler;
|
||||
import com.intellij.execution.process.CapturingAnsiEscapesAwareProcessHandler;
|
||||
import com.intellij.util.io.BaseOutputReader;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class ZigCapturingProcessHandler extends CapturingProcessHandler {
|
||||
public class ZigCapturingProcessHandler extends CapturingAnsiEscapesAwareProcessHandler {
|
||||
public static Optional<ZigCapturingProcessHandler> startProcess(GeneralCommandLine commandLine) {
|
||||
try {
|
||||
return Optional.of(new ZigCapturingProcessHandler(commandLine));
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package com.falsepattern.zigbrains.project.execution.base;
|
||||
|
||||
import com.falsepattern.zigbrains.project.execution.base.ZigExecConfigBase;
|
||||
import com.falsepattern.zigbrains.zig.parser.ZigFile;
|
||||
import com.intellij.execution.actions.ConfigurationContext;
|
||||
import com.intellij.execution.actions.LazyRunConfigurationProducer;
|
||||
|
@ -27,6 +26,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 +48,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 +66,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 +96,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,10 +16,10 @@
|
|||
|
||||
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;
|
||||
import com.intellij.build.BuildTextConsoleView;
|
||||
import com.intellij.execution.DefaultExecutionResult;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.CommandLineState;
|
||||
|
@ -31,6 +31,7 @@ import lombok.val;
|
|||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
|
||||
public abstract class ProfileStateBase<T extends ZigExecConfigBase<T>> extends CommandLineState {
|
||||
protected final T configuration;
|
||||
|
@ -42,18 +43,20 @@ public abstract class ProfileStateBase<T extends ZigExecConfigBase<T>> extends C
|
|||
|
||||
@Override
|
||||
protected @NotNull ProcessHandler startProcess() throws ExecutionException {
|
||||
return new ZigCapturingProcessHandler(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() {
|
||||
|
@ -63,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 = getConsoleBuilder().getConsole();
|
||||
val console = new BuildTextConsoleView(environment.getProject(), false, Collections.emptyList());
|
||||
console.attachToProcess(handler);
|
||||
return new DefaultExecutionResult(console, handler);
|
||||
}
|
||||
|
|
|
@ -18,75 +18,462 @@ 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.Objects;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static com.intellij.ui.dsl.builder.BuilderKt.panel;
|
||||
|
||||
public class ZigConfigEditor<T extends ZigExecConfigBase<T>> extends SettingsEditor<T> {
|
||||
protected final LabeledComponent<TextFieldWithBrowseButton> workingDirectoryComponent =
|
||||
new WorkingDirectoryComponent();
|
||||
private final ZigExecConfigBase<T> state;
|
||||
private final List<ZigConfigurable.ZigConfigModule<?>> configModules = new ArrayList<>();
|
||||
|
||||
public ZigConfigEditor(ZigExecConfigBase<T> state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyEditorTo(@NotNull T s) throws ConfigurationException {
|
||||
s.workingDirectory = Paths.get(workingDirectoryComponent.getComponent().getText());
|
||||
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) {
|
||||
workingDirectoryComponent.getComponent().setText(Objects.requireNonNullElse(s.workingDirectory, "").toString());
|
||||
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) -> {
|
||||
constructPanel(p);
|
||||
for (val module: configModules) {
|
||||
module.construct(p);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
protected void constructPanel(Panel p) {
|
||||
p.row(workingDirectoryComponent.getLabel(), (r) -> {
|
||||
r.cell(workingDirectoryComponent).resizableColumn().align(AlignX.FILL).align(AlignY.FILL);
|
||||
return null;
|
||||
});
|
||||
@Override
|
||||
protected void disposeEditor() {
|
||||
for (val module: configModules) {
|
||||
module.dispose();
|
||||
}
|
||||
configModules.clear();
|
||||
}
|
||||
|
||||
public static abstract class WithFilePath<T extends ZigExecConfigBase<T>> extends ZigConfigEditor<T> {
|
||||
private final ZigFilePathPanel filePathPanel = new ZigFilePathPanel();
|
||||
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
|
||||
protected void applyEditorTo(@NotNull T s) throws ConfigurationException {
|
||||
super.applyEditorTo(s);
|
||||
setFilePath(s, filePathPanel.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;
|
||||
}
|
||||
|
||||
default boolean tryReset(ZigConfigurable<?> cfg) {
|
||||
val x = tryMatch(cfg);
|
||||
if (x != null) {
|
||||
reset(x);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void construct(Panel p);
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class PathConfigurable<T extends PathConfigurable<T>> implements ZigConfigurable<T> {
|
||||
private @Nullable Path path = null;
|
||||
|
||||
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
|
||||
protected void resetEditorFrom(@NotNull T s) {
|
||||
super.resetEditorFrom(s);
|
||||
filePathPanel.setText(Objects.requireNonNullElse(getFilePath(s), ""));
|
||||
public void readExternal(@NotNull Element element) {
|
||||
try {
|
||||
ElementUtil.readString(element, getSerializedName()).map(Paths::get).ifPresent(x -> path = x);
|
||||
} catch (InvalidPathException ignored){}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void constructPanel(Panel p) {
|
||||
super.constructPanel(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());
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
@Getter(AccessLevel.PROTECTED)
|
||||
@RequiredArgsConstructor
|
||||
public static class WorkDirectoryConfigurable extends PathConfigurable<WorkDirectoryConfigurable> {
|
||||
private transient final String serializedName;
|
||||
|
||||
@Override
|
||||
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 writeExternal(@NotNull Element element) {
|
||||
ElementUtil.writeBoolean(element, serializedName, colored);
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
protected abstract String getFilePath(T config);
|
||||
protected abstract void setFilePath(T config, String path);
|
||||
@Override
|
||||
public void writeExternal(@NotNull Element element) {
|
||||
val child = ElementUtil.writeChild(element, serializedName);
|
||||
ElementUtil.writeEnum(child, "level", level);
|
||||
ElementUtil.writeBoolean(child, "forced", forced);
|
||||
}
|
||||
|
||||
@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() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,24 +22,60 @@ import com.intellij.execution.configurations.ConfigurationFactory;
|
|||
import com.intellij.execution.configurations.LocatableConfigurationBase;
|
||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectUtil;
|
||||
import com.intellij.openapi.util.InvalidDataException;
|
||||
import com.intellij.openapi.util.NlsActions;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import lombok.Getter;
|
||||
import lombok.val;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.file.Path;
|
||||
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(getProject().isDefault() ? null : Optional.ofNullable(ProjectUtil.guessProjectDir(getProject()))
|
||||
.map(VirtualFile::toNioPath)
|
||||
.orElse(null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ZigConfigEditor<T> getConfigurationEditor() {
|
||||
return new ZigConfigEditor<>(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readExternal(@NotNull Element element) throws InvalidDataException {
|
||||
super.readExternal(element);
|
||||
getConfigurables().forEach(cfg -> cfg.readExternal(element));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeExternal(@NotNull Element element) {
|
||||
super.writeExternal(element);
|
||||
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();
|
||||
|
||||
|
@ -47,4 +83,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 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,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.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.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,41 +16,33 @@
|
|||
|
||||
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.val;
|
||||
import org.apache.groovy.util.Arrays;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
public class ZigExecConfigBuild extends ZigExecConfigBase<ZigExecConfigBuild> {
|
||||
public String extraArguments = "";
|
||||
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"};
|
||||
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
|
||||
|
@ -59,54 +51,21 @@ public class ZigExecConfigBuild extends ZigExecConfigBase<ZigExecConfigBuild> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Editor getConfigurationEditor() {
|
||||
return new Editor();
|
||||
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);
|
||||
|
||||
val extraArguments = ElementUtil.readString(element, "extraArguments");
|
||||
if (extraArguments != null) {
|
||||
this.extraArguments = extraArguments;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeExternal(@NotNull Element element) {
|
||||
super.writeExternal(element);
|
||||
|
||||
ElementUtil.writeString(element, "extraArguments", extraArguments);
|
||||
}
|
||||
|
||||
public static class Editor extends ZigConfigEditor<ZigExecConfigBuild> {
|
||||
private final JBTextField extraArgs = new JBTextField();
|
||||
|
||||
@Override
|
||||
protected void applyEditorTo(@NotNull ZigExecConfigBuild s) throws ConfigurationException {
|
||||
super.applyEditorTo(s);
|
||||
s.extraArguments = extraArgs.getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetEditorFrom(@NotNull ZigExecConfigBuild s) {
|
||||
super.resetEditorFrom(s);
|
||||
extraArgs.setText(Objects.requireNonNullElse(s.extraArguments, ""));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void constructPanel(Panel p) {
|
||||
super.constructPanel(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,31 +16,42 @@
|
|||
|
||||
package com.falsepattern.zigbrains.project.execution.run;
|
||||
|
||||
import com.falsepattern.zigbrains.project.execution.base.ZigExecConfigBase;
|
||||
import com.falsepattern.zigbrains.common.util.CollectionUtil;
|
||||
import com.falsepattern.zigbrains.project.execution.base.ZigConfigEditor;
|
||||
import com.falsepattern.zigbrains.project.util.ElementUtil;
|
||||
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 com.intellij.openapi.util.InvalidDataException;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.jdom.Element;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@Setter
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
public class ZigExecConfigRun extends ZigExecConfigBase<ZigExecConfigRun> {
|
||||
public String filePath = "";
|
||||
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", 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
|
||||
|
@ -49,42 +60,22 @@ public class ZigExecConfigRun extends ZigExecConfigBase<ZigExecConfigRun> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Editor getConfigurationEditor() {
|
||||
return new Editor();
|
||||
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);
|
||||
|
||||
var filePath = ElementUtil.readString(element, "filePath");
|
||||
if (filePath != null) {
|
||||
this.filePath = filePath;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeExternal(@NotNull Element element) {
|
||||
super.writeExternal(element);
|
||||
|
||||
ElementUtil.writeString(element, "filePath", filePath);
|
||||
}
|
||||
|
||||
public static class Editor extends ZigConfigEditor.WithFilePath<ZigExecConfigRun> {
|
||||
|
||||
@Override
|
||||
protected String getFilePath(ZigExecConfigRun config) {
|
||||
return config.filePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setFilePath(ZigExecConfigRun config, String path) {
|
||||
config.filePath = path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,27 +16,42 @@
|
|||
|
||||
package com.falsepattern.zigbrains.project.execution.test;
|
||||
|
||||
import com.falsepattern.zigbrains.common.util.CollectionUtil;
|
||||
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.execution.base.ZigExecConfigBase;
|
||||
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 lombok.Getter;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
public class ZigExecConfigTest extends ZigExecConfigBase<ZigExecConfigTest> {
|
||||
public String filePath = "";
|
||||
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", 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
|
||||
|
@ -50,20 +65,16 @@ public class ZigExecConfigTest extends ZigExecConfigBase<ZigExecConfigTest> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public @NotNull SettingsEditor<? extends RunConfiguration> getConfigurationEditor() {
|
||||
return new Editor();
|
||||
public ZigExecConfigTest clone() {
|
||||
val clone = super.clone();
|
||||
clone.filePath = filePath.clone();
|
||||
clone.colored = colored.clone();
|
||||
clone.optimization = optimization.clone();
|
||||
return clone;
|
||||
}
|
||||
|
||||
public static class Editor extends ZigConfigEditor.WithFilePath<ZigExecConfigTest> {
|
||||
|
||||
@Override
|
||||
protected String getFilePath(ZigExecConfigTest config) {
|
||||
return config.filePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setFilePath(ZigExecConfigTest config, String path) {
|
||||
config.filePath = path;
|
||||
}
|
||||
@Override
|
||||
public @NotNull List<ZigConfigEditor.ZigConfigurable<?>> getConfigurables() {
|
||||
return CollectionUtil.concat(super.getConfigurables(), filePath, optimization, colored);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ import com.intellij.ide.util.projectWizard.CustomStepProjectGenerator;
|
|||
import com.intellij.notification.Notification;
|
||||
import com.intellij.notification.NotificationType;
|
||||
import com.intellij.notification.Notifications;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.WriteAction;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.project.Project;
|
||||
|
|
|
@ -16,16 +16,19 @@
|
|||
|
||||
package com.falsepattern.zigbrains.project.runconfig;
|
||||
|
||||
import com.falsepattern.zigbrains.common.util.StringUtil;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.intellij.execution.configurations.PtyCommandLine;
|
||||
import com.intellij.execution.process.KillableProcessHandler;
|
||||
import com.intellij.execution.process.AnsiEscapeDecoder;
|
||||
import com.intellij.execution.process.KillableColoredProcessHandler;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.pty4j.PtyProcess;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
public class ZigProcessHandler extends KillableProcessHandler {
|
||||
public class ZigProcessHandler extends KillableColoredProcessHandler implements AnsiEscapeDecoder.ColoredTextAcceptor {
|
||||
public ZigProcessHandler(@NotNull GeneralCommandLine commandLine) throws ExecutionException {
|
||||
super(commandLine);
|
||||
setHasPty(commandLine instanceof PtyCommandLine);
|
||||
|
@ -37,4 +40,9 @@ public class ZigProcessHandler extends KillableProcessHandler {
|
|||
setHasPty(process instanceof PtyProcess);
|
||||
setShouldDestroyProcessRecursively(!hasPty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void coloredTextAvailable(@NotNull String text, @NotNull Key attributes) {
|
||||
super.coloredTextAvailable(StringUtil.translateVT100Escapes(text), attributes);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,9 +22,9 @@ import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
|||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.RunProfile;
|
||||
import com.intellij.execution.executors.DefaultRunExecutor;
|
||||
import com.intellij.execution.runners.DefaultProgramRunnerKt;
|
||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||
import com.intellij.execution.ui.RunContentDescriptor;
|
||||
import com.intellij.execution.runners.DefaultProgramRunnerKt;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,19 +22,18 @@ import com.falsepattern.zigbrains.zig.environment.ZLSConfigProvider;
|
|||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectUtil;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class ToolchainZLSConfigProvider implements ZLSConfigProvider {
|
||||
@Override
|
||||
public @NotNull ZLSConfig getEnvironment(Project project) {
|
||||
public void getEnvironment(Project project, ZLSConfig.ZLSConfigBuilder builder) {
|
||||
val projectSettings = ZigProjectSettingsService.getInstance(project);
|
||||
val toolchain = projectSettings.getToolchain();
|
||||
if (toolchain == null)
|
||||
return ZLSConfig.EMPTY;
|
||||
return;
|
||||
val projectDir = ProjectUtil.guessProjectDir(project);
|
||||
val env = toolchain.zig().getEnv(projectDir == null ? Path.of(".") : projectDir.toNioPath());
|
||||
return env.map(e -> new ZLSConfig(e.zigExecutable(), e.libDirectory())).orElse(ZLSConfig.EMPTY);
|
||||
env.ifPresent(e -> builder.zig_exe_path(e.zigExecutable()).zig_lib_path(e.libDirectory()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ package com.falsepattern.zigbrains.project.toolchain.tools;
|
|||
|
||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainEnvironmentSerializable;
|
||||
import com.falsepattern.zigbrains.project.util.CLIUtil;
|
||||
import com.google.gson.Gson;
|
||||
import com.intellij.execution.process.ProcessOutput;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
|
|
@ -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,7 +20,6 @@ import com.intellij.openapi.fileChooser.FileChooserDescriptor;
|
|||
import com.intellij.openapi.ui.TextBrowseFolderListener;
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||
import com.intellij.ui.TextAccessor;
|
||||
import com.intellij.ui.components.JBTextField;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
import java.awt.BorderLayout;
|
||||
|
|
|
@ -20,12 +20,15 @@ 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.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 +51,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,10 +18,12 @@ 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 {
|
||||
public static @Nullable String readString(Element element, String name) {
|
||||
public static Optional<String> readString(Element element, String name) {
|
||||
return element.getChildren()
|
||||
.stream()
|
||||
.filter(it -> it.getName()
|
||||
|
@ -29,15 +31,82 @@ public class ElementUtil {
|
|||
it.getAttributeValue("name")
|
||||
.equals(name))
|
||||
.findAny()
|
||||
.map(it -> it.getAttributeValue("value"))
|
||||
.orElse(null);
|
||||
.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);
|
||||
}
|
||||
|
||||
public static void writeBoolean(Element element, String name, boolean state) {
|
||||
writeString(element, name, Boolean.toString(state));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue