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
|
||||||
- Debugging support for tests when launched using the ZigTest task type (and with the gutter icons in the editor)
|
- 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
|
- Project
|
||||||
- Added `zig init` as a new project creation option
|
- Added `zig init` as a new project creation option
|
||||||
|
@ -30,6 +31,9 @@ Changelog structure reference:
|
||||||
- Zig
|
- Zig
|
||||||
- Updated semantic highlighting to latest ZLS protocol
|
- Updated semantic highlighting to latest ZLS protocol
|
||||||
|
|
||||||
|
- ZLS
|
||||||
|
- ZLS configuration is now partially editable through the GUI
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Project
|
- Project
|
||||||
|
@ -38,6 +42,14 @@ Changelog structure reference:
|
||||||
- Plugin
|
- Plugin
|
||||||
- Removed a bunch of write action locking, the editor should feel more responsive now
|
- 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]
|
## [12.0.0]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -21,10 +21,13 @@ IC modules MUST NOT depend on CL modules, as this violates the restrictions set
|
||||||
|
|
||||||
### Common (IC)
|
### Common (IC)
|
||||||
|
|
||||||
### LSP (IC)
|
### LSP-Common (IC)
|
||||||
- LSP4J (EXT)
|
- LSP4J (EXT)
|
||||||
- Flexmark (EXT)
|
|
||||||
|
### LSP (IC)
|
||||||
- Apache Commons Lang 3 (EXT)
|
- Apache Commons Lang 3 (EXT)
|
||||||
|
- Common (IC)
|
||||||
|
- LSP-Common (IC)
|
||||||
|
|
||||||
### Zig (IC)
|
### Zig (IC)
|
||||||
- Grammarkit (EXT)
|
- Grammarkit (EXT)
|
||||||
|
@ -39,6 +42,8 @@ IC modules MUST NOT depend on CL modules, as this violates the restrictions set
|
||||||
- Grammarkit (EXT)
|
- Grammarkit (EXT)
|
||||||
- Common (IC)
|
- Common (IC)
|
||||||
|
|
||||||
### Debugger (CL)
|
### Debugger (IU/CL)
|
||||||
|
- Common (IC)
|
||||||
|
- LSP-Common (IC)
|
||||||
- Zig (IC)
|
- Zig (IC)
|
||||||
- Project (IC)
|
- Project (IC)
|
16
README.md
16
README.md
|
@ -13,11 +13,12 @@ complain about missing files
|
||||||
|
|
||||||
## Special Thanks
|
## 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),
|
- [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
|
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
|
- The members of the `Zig Programming Language` discord server's `#tooling-dev` channel for providing encouragement,
|
||||||
feedback
|
feedback, and lots of bug reports.
|
||||||
|
|
||||||
- The Ballerina Platform developers for `lsp4intellij`, the language server connector between the IntelliJ platform
|
- The Ballerina Platform developers for `lsp4intellij`, the language server connector between the IntelliJ platform
|
||||||
and the Eclipse LSP4J project
|
and the Eclipse LSP4J project
|
||||||
|
@ -80,7 +81,16 @@ LSP server is running.
|
||||||
|
|
||||||
## Debugging
|
## 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.
|
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.
|
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:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns:svg="http://www.w3.org/2000/svg">
|
>
|
||||||
<defs
|
<defs
|
||||||
id="defs36">
|
id="defs36">
|
||||||
<linearGradient
|
<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 {
|
return provider {
|
||||||
System.getenv("RELEASE_VERSION")
|
|
||||||
}.orElse(provider {
|
|
||||||
try {
|
try {
|
||||||
gitVersion()
|
gitVersion()
|
||||||
} catch (_: java.lang.Exception) {
|
} catch (_: java.lang.Exception) {
|
||||||
error("Git version not found and RELEASE_VERSION environment variable is not set!")
|
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
|
targetCompatibility = javaVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.withType(JavaCompile::class) {
|
||||||
|
options.encoding = "UTF-8"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
group = properties("pluginGroup").get()
|
group = properties("pluginGroup").get()
|
||||||
version = pluginVersionFull().get()
|
version = pluginVersionFull().get()
|
||||||
|
|
||||||
|
@ -169,6 +185,10 @@ project(":debugger") {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":zig"))
|
implementation(project(":zig"))
|
||||||
implementation(project(":project"))
|
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 {
|
intellij {
|
||||||
version = clionVersion
|
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") {
|
project(":lsp") {
|
||||||
apply {
|
apply {
|
||||||
plugin("java-library")
|
plugin("java-library")
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
api("org.eclipse.lsp4j:org.eclipse.lsp4j:0.22.0")
|
implementation(project(":common"))
|
||||||
implementation("com.vladsch.flexmark:flexmark:0.64.8")
|
api(project(":lsp-common"))
|
||||||
api("org.apache.commons:commons-lang3:3.14.0")
|
api("org.apache.commons:commons-lang3:3.14.0")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,18 +13,17 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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.ApplicationManager;
|
||||||
import com.intellij.openapi.application.ex.ApplicationManagerEx;
|
|
||||||
import com.intellij.openapi.project.NoAccessDuringPsiEvents;
|
import com.intellij.openapi.project.NoAccessDuringPsiEvents;
|
||||||
import com.intellij.openapi.util.Computable;
|
import com.intellij.openapi.util.Computable;
|
||||||
import com.intellij.openapi.util.Condition;
|
import lombok.val;
|
||||||
|
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
public class ApplicationUtils {
|
public class ApplicationUtil {
|
||||||
|
|
||||||
private final static ExecutorService EXECUTOR_SERVICE;
|
private final static ExecutorService EXECUTOR_SERVICE;
|
||||||
|
|
||||||
|
@ -50,16 +49,7 @@ public class ApplicationUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
static public <T> T computableReadAction(Computable<T> computable) {
|
static public <T> T computableReadAction(Computable<T> computable) {
|
||||||
if (ApplicationManager.getApplication().isDispatchThread() ||
|
|
||||||
ApplicationManagerEx.getApplicationEx().holdsReadLock()) {
|
|
||||||
return ApplicationManager.getApplication().runReadAction(computable);
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static public void writeAction(Runnable runnable) {
|
static public void writeAction(Runnable runnable) {
|
||||||
|
@ -70,15 +60,15 @@ public class ApplicationUtils {
|
||||||
return ApplicationManager.getApplication().runWriteAction(computable);
|
return ApplicationManager.getApplication().runWriteAction(computable);
|
||||||
}
|
}
|
||||||
|
|
||||||
static public void invokeAfterPsiEvents(Runnable runnable) {
|
static public void invokeAfterPsiEvents(Runnable runnable, boolean readLock, boolean writeLock) {
|
||||||
Runnable wrapper = () -> {
|
Runnable wrapper = () -> {
|
||||||
if (NoAccessDuringPsiEvents.isInsideEventProcessing()) {
|
if (NoAccessDuringPsiEvents.isInsideEventProcessing()) {
|
||||||
invokeAfterPsiEvents(runnable);
|
invokeAfterPsiEvents(runnable, readLock, writeLock);
|
||||||
} else {
|
} else {
|
||||||
runnable.run();
|
runnable.run();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
val app = ApplicationManager.getApplication();
|
||||||
ApplicationManager.getApplication().invokeLater(wrapper, (Condition<Void>) value -> false);
|
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;
|
package com.falsepattern.zigbrains.common.util;
|
||||||
|
|
||||||
|
import lombok.val;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class StringUtil {
|
public class StringUtil {
|
||||||
public static String blankToNull(String value) {
|
public static String blankToNull(String value) {
|
||||||
return value == null || value.isBlank() ? null : value;
|
return value == null || value.isBlank() ? null : value;
|
||||||
|
@ -24,4 +28,56 @@ public class StringUtil {
|
||||||
public static String orEmpty(String value) {
|
public static String orEmpty(String value) {
|
||||||
return value == null ? "" : 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);
|
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,
|
public static TextFieldWithBrowseButton pathTextField(FileChooserDescriptor fileChooserDescriptor,
|
||||||
Disposable disposable,
|
Disposable disposable,
|
||||||
@NlsContexts.DialogTitle String dialogTitle,
|
@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.
|
* 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.diagnostic.Logger;
|
||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
import com.jetbrains.cidr.cpp.execution.debugger.backend.CLionGDBDriverConfiguration;
|
import com.jetbrains.cidr.cpp.execution.debugger.backend.CLionGDBDriverConfiguration;
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* 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.extensions.ExtensionPointName;
|
||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
|
@ -14,13 +14,15 @@
|
||||||
* limitations under the License.
|
* 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.Notification;
|
||||||
import com.intellij.notification.NotificationType;
|
import com.intellij.notification.NotificationType;
|
||||||
import com.intellij.notification.Notifications;
|
import com.intellij.notification.Notifications;
|
||||||
import com.intellij.openapi.project.Project;
|
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.DebuggerDriverConfiguration;
|
||||||
import com.jetbrains.cidr.execution.debugger.backend.lldb.LLDBDriverConfiguration;
|
import com.jetbrains.cidr.execution.debugger.backend.lldb.LLDBDriverConfiguration;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
|
@ -28,6 +30,9 @@ import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
public class Utils {
|
public class Utils {
|
||||||
public static @Nullable DebuggerDriverConfiguration getDebuggerConfiguration(Project project) {
|
public static @Nullable DebuggerDriverConfiguration getDebuggerConfiguration(Project project) {
|
||||||
|
if (OS.CURRENT == OS.Windows) {
|
||||||
|
return new WinDebuggerDriverConfiguration();
|
||||||
|
}
|
||||||
val providedDebugger = DebuggerDriverProvider.findDebuggerConfigurations(project)
|
val providedDebugger = DebuggerDriverProvider.findDebuggerConfigurations(project)
|
||||||
.filter(x -> x instanceof DebuggerDriverConfiguration)
|
.filter(x -> x instanceof DebuggerDriverConfiguration)
|
||||||
.map(x -> (DebuggerDriverConfiguration)x)
|
.map(x -> (DebuggerDriverConfiguration)x)
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.falsepattern.zigbrains.zig.debugger;
|
package com.falsepattern.zigbrains.debugger;
|
||||||
|
|
||||||
import com.jetbrains.cidr.execution.debugger.CidrDebuggerEditorsExtensionBase;
|
import com.jetbrains.cidr.execution.debugger.CidrDebuggerEditorsExtensionBase;
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.falsepattern.zigbrains.zig.debugger;
|
package com.falsepattern.zigbrains.debugger;
|
||||||
|
|
||||||
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriver;
|
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriver;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* 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.falsepattern.zigbrains.project.execution.base.ZigExecConfigBase;
|
||||||
import com.intellij.execution.configurations.RunProfile;
|
import com.intellij.execution.configurations.RunProfile;
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.falsepattern.zigbrains.zig.debugger;
|
package com.falsepattern.zigbrains.debugger;
|
||||||
|
|
||||||
import com.falsepattern.zigbrains.zig.ZigFileType;
|
import com.falsepattern.zigbrains.zig.ZigFileType;
|
||||||
import com.intellij.openapi.fileTypes.FileType;
|
import com.intellij.openapi.fileTypes.FileType;
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.falsepattern.zigbrains.zig.debugger;
|
package com.falsepattern.zigbrains.debugger;
|
||||||
|
|
||||||
import com.intellij.execution.ExecutionException;
|
import com.intellij.execution.ExecutionException;
|
||||||
import com.intellij.execution.filters.Filter;
|
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.
|
* 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.execution.base.ProfileStateBase;
|
||||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
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.RunParameters;
|
||||||
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration;
|
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
@ -38,6 +40,6 @@ public abstract class ZigDebugParametersBase<ProfileState extends ProfileStateBa
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable String getArchitectureId() {
|
public @Nullable String getArchitectureId() {
|
||||||
return null;
|
return ArchitectureType.forVmCpuArch(CpuArch.CURRENT).getId();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,13 +14,13 @@
|
||||||
* limitations under the License.
|
* 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.execution.base.ProfileStateBase;
|
||||||
import com.falsepattern.zigbrains.project.runconfig.ZigProgramRunnerBase;
|
import com.falsepattern.zigbrains.project.runconfig.ZigProgramRunnerBase;
|
||||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||||
import com.falsepattern.zigbrains.zig.debugger.Utils;
|
import com.falsepattern.zigbrains.debugger.Utils;
|
||||||
import com.falsepattern.zigbrains.zig.debugger.ZigLocalDebugProcess;
|
import com.falsepattern.zigbrains.debugger.ZigLocalDebugProcess;
|
||||||
import com.intellij.execution.ExecutionException;
|
import com.intellij.execution.ExecutionException;
|
||||||
import com.intellij.execution.configurations.RunProfile;
|
import com.intellij.execution.configurations.RunProfile;
|
||||||
import com.intellij.execution.executors.DefaultDebugExecutor;
|
import com.intellij.execution.executors.DefaultDebugExecutor;
|
|
@ -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.
|
* 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.execution.run.ProfileStateRun;
|
||||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
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.Installer;
|
||||||
import com.jetbrains.cidr.execution.TrivialInstaller;
|
|
||||||
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration;
|
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
@ -31,6 +31,11 @@ public class ZigDebugParametersRun extends ZigDebugParametersBase<ProfileStateRu
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull Installer getInstaller() {
|
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.
|
* 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.base.ProfileStateBase;
|
||||||
import com.falsepattern.zigbrains.project.execution.run.ProfileStateRun;
|
import com.falsepattern.zigbrains.project.execution.run.ProfileStateRun;
|
||||||
import com.falsepattern.zigbrains.project.execution.run.ZigExecConfigRun;
|
import com.falsepattern.zigbrains.project.execution.run.ZigExecConfigRun;
|
||||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||||
import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain;
|
import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain;
|
||||||
import com.falsepattern.zigbrains.zig.debugger.base.ZigDebugRunnerBase;
|
|
||||||
import com.intellij.execution.configurations.RunProfile;
|
import com.intellij.execution.configurations.RunProfile;
|
||||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||||
import com.intellij.notification.Notification;
|
import com.intellij.notification.Notification;
|
|
@ -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.
|
* 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.base.ProfileStateBase;
|
||||||
import com.falsepattern.zigbrains.project.execution.test.ProfileStateTest;
|
import com.falsepattern.zigbrains.project.execution.test.ProfileStateTest;
|
||||||
import com.falsepattern.zigbrains.project.execution.test.ZigExecConfigTest;
|
import com.falsepattern.zigbrains.project.execution.test.ZigExecConfigTest;
|
||||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||||
import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain;
|
import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain;
|
||||||
import com.falsepattern.zigbrains.zig.debugger.base.ZigDebugParametersBase;
|
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugParametersBase;
|
||||||
import com.falsepattern.zigbrains.zig.debugger.base.ZigDebugRunnerBase;
|
import com.falsepattern.zigbrains.debugger.runner.base.ZigDebugRunnerBase;
|
||||||
import com.intellij.execution.configurations.RunProfile;
|
import com.intellij.execution.configurations.RunProfile;
|
||||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||||
import com.intellij.notification.Notification;
|
import com.intellij.notification.Notification;
|
|
@ -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.
|
~ 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>
|
<depends>com.intellij.modules.clion</depends>
|
||||||
<extensions defaultExtensionNs="com.falsepattern.zigbrains">
|
<extensions defaultExtensionNs="com.falsepattern.zigbrains">
|
||||||
<debuggerDriverProvider implementation="com.falsepattern.zigbrains.zig.cpp.CPPDebuggerDriverProvider"/>
|
<debuggerDriverProvider implementation="com.falsepattern.zigbrains.cpp.CPPDebuggerDriverProvider"/>
|
||||||
</extensions>
|
</extensions>
|
||||||
|
|
||||||
</idea-plugin>
|
</idea-plugin>
|
|
@ -14,29 +14,37 @@
|
||||||
~ limitations under the License.
|
~ 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>
|
<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">
|
<extensions defaultExtensionNs="com.intellij">
|
||||||
<programRunner implementation="com.falsepattern.zigbrains.zig.debugger.run.ZigDebugRunnerRun"
|
<programRunner implementation="com.falsepattern.zigbrains.debugger.runner.run.ZigDebugRunnerRun"
|
||||||
id="ZigDebugRunnerRun"/>
|
id="ZigDebugRunnerRun"/>
|
||||||
<programRunner implementation="com.falsepattern.zigbrains.zig.debugger.test.ZigDebugRunnerTest"
|
<programRunner implementation="com.falsepattern.zigbrains.debugger.runner.test.ZigDebugRunnerTest"
|
||||||
id="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"
|
<notificationGroup displayType="BALLOON"
|
||||||
bundle="zigbrains.zig.debugger.Bundle"
|
bundle="zigbrains.debugger.Bundle"
|
||||||
key="notif-debug-error"
|
key="notif-debug-error"
|
||||||
id="ZigBrains.Debugger.Error"/>
|
id="ZigBrains.Debugger.Error"/>
|
||||||
<notificationGroup displayType="BALLOON"
|
<notificationGroup displayType="BALLOON"
|
||||||
bundle="zigbrains.zig.debugger.Bundle"
|
bundle="zigbrains.debugger.Bundle"
|
||||||
key="notif-debug-warn"
|
key="notif-debug-warn"
|
||||||
id="ZigBrains.Debugger.Warn"/>
|
id="ZigBrains.Debugger.Warn"/>
|
||||||
|
<applicationConfigurable parentId="project.propDebugger"
|
||||||
|
instance="com.falsepattern.zigbrains.debugger.win.config.WinDebuggerConfigurable"
|
||||||
|
displayName="Zig (Windows)"/>
|
||||||
</extensions>
|
</extensions>
|
||||||
|
|
||||||
<extensions defaultExtensionNs="cidr.debugger">
|
<extensions defaultExtensionNs="cidr.debugger">
|
||||||
<languageSupport language="Zig" implementationClass="com.falsepattern.zigbrains.zig.debugger.ZigDebuggerLanguageSupport"/>
|
<languageSupport language="Zig" implementationClass="com.falsepattern.zigbrains.debugger.ZigDebuggerLanguageSupport"/>
|
||||||
<editorsExtension language="Zig" implementationClass="com.falsepattern.zigbrains.zig.debugger.ZigDebuggerEditorsExtension"/>
|
<editorsExtension language="Zig" implementationClass="com.falsepattern.zigbrains.debugger.ZigDebuggerEditorsExtension"/>
|
||||||
<lineBreakpointFileTypesProvider implementation="com.falsepattern.zigbrains.zig.debugger.ZigLineBreakpointFileTypesProvider"/>
|
<lineBreakpointFileTypesProvider implementation="com.falsepattern.zigbrains.debugger.ZigLineBreakpointFileTypesProvider"/>
|
||||||
|
<localVariablesFilterHandler implementation="com.falsepattern.zigbrains.debugger.ZigVariablesFilterHandler"/>
|
||||||
</extensions>
|
</extensions>
|
||||||
</idea-plugin>
|
</idea-plugin>
|
|
@ -13,7 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package com.falsepattern.zigbrains.lsp.client.connection;
|
package com.falsepattern.zigbrains.lspcommon.connection;
|
||||||
|
|
||||||
import com.intellij.openapi.diagnostic.Logger;
|
import com.intellij.openapi.diagnostic.Logger;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
@ -40,7 +40,7 @@ public class ProcessStreamConnectionProvider implements StreamConnectionProvider
|
||||||
@Nullable
|
@Nullable
|
||||||
private ProcessBuilder builder;
|
private ProcessBuilder builder;
|
||||||
@Nullable
|
@Nullable
|
||||||
private Process process = null;
|
protected Process process = null;
|
||||||
private List<String> commands;
|
private List<String> commands;
|
||||||
private String workingDir;
|
private String workingDir;
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ public class ProcessStreamConnectionProvider implements StreamConnectionProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() throws IOException {
|
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());
|
throw new IOException("Unable to start language server: " + this.toString());
|
||||||
}
|
}
|
||||||
ProcessBuilder builder = createProcessBuilder();
|
ProcessBuilder builder = createProcessBuilder();
|
||||||
|
@ -86,6 +86,12 @@ public class ProcessStreamConnectionProvider implements StreamConnectionProvider
|
||||||
return process != null ? process.getInputStream() : null;
|
return process != null ? process.getInputStream() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public InputStream getErrorStream() {
|
||||||
|
return process != null ? process.getErrorStream() : null;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public OutputStream getOutputStream() {
|
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
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (obj instanceof ProcessStreamConnectionProvider) {
|
if (obj instanceof ProcessStreamConnectionProvider) {
|
|
@ -13,8 +13,9 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
@ -25,6 +26,10 @@ public interface StreamConnectionProvider {
|
||||||
|
|
||||||
InputStream getInputStream();
|
InputStream getInputStream();
|
||||||
|
|
||||||
|
default @Nullable InputStream getErrorStream() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
OutputStream getOutputStream();
|
OutputStream getOutputStream();
|
||||||
|
|
||||||
void stop();
|
void stop();
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package com.falsepattern.zigbrains.lsp;
|
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.ServerStatus;
|
||||||
import com.falsepattern.zigbrains.lsp.client.languageserver.serverdefinition.LanguageServerDefinition;
|
import com.falsepattern.zigbrains.lsp.client.languageserver.serverdefinition.LanguageServerDefinition;
|
||||||
import com.falsepattern.zigbrains.lsp.client.languageserver.wrapper.LanguageServerWrapper;
|
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.fileEditor.FileDocumentManager;
|
||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
|
import lombok.val;
|
||||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||||
import org.apache.commons.lang3.tuple.MutablePair;
|
import org.apache.commons.lang3.tuple.MutablePair;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
@ -43,7 +45,7 @@ import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.function.Predicate;
|
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 {
|
public class IntellijLanguageClient {
|
||||||
|
|
||||||
|
@ -330,9 +332,14 @@ public class IntellijLanguageClient {
|
||||||
public static void removeWrapper(LanguageServerWrapper wrapper) {
|
public static void removeWrapper(LanguageServerWrapper wrapper) {
|
||||||
if (wrapper.getProject() != null) {
|
if (wrapper.getProject() != null) {
|
||||||
String[] extensions = wrapper.getServerDefinition().ext.split(LanguageServerDefinition.SPLIT_CHAR);
|
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) {
|
for (String ext : extensions) {
|
||||||
MutablePair<String, String> extProjectPair = new MutablePair<>(ext, FileUtils.pathToUri(
|
MutablePair<String, String> extProjectPair = new MutablePair<>(ext, absolutePath);
|
||||||
new File(wrapper.getProjectRootPath()).getAbsolutePath()));
|
|
||||||
extToLanguageWrapper.remove(extProjectPair);
|
extToLanguageWrapper.remove(extProjectPair);
|
||||||
extToServerDefinition.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.IntellijLanguageClient;
|
||||||
import com.falsepattern.zigbrains.lsp.editor.EditorEventManager;
|
import com.falsepattern.zigbrains.lsp.editor.EditorEventManager;
|
||||||
import com.falsepattern.zigbrains.lsp.editor.EditorEventManagerBase;
|
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.codeInsight.navigation.actions.GotoDeclarationAction;
|
||||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||||
import com.intellij.openapi.editor.Editor;
|
import com.intellij.openapi.editor.Editor;
|
||||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
|
||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
import com.intellij.psi.PsiDocumentManager;
|
import com.intellij.psi.PsiDocumentManager;
|
||||||
import com.intellij.psi.PsiFile;
|
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.EditorEventManager;
|
||||||
import com.falsepattern.zigbrains.lsp.editor.EditorEventManagerBase;
|
import com.falsepattern.zigbrains.lsp.editor.EditorEventManagerBase;
|
||||||
import com.intellij.codeInsight.hint.actions.ShowImplementationsAction;
|
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.AnActionEvent;
|
||||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||||
import com.intellij.openapi.editor.Editor;
|
import com.intellij.openapi.editor.Editor;
|
||||||
import com.intellij.openapi.project.DumbAware;
|
|
||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
import com.intellij.psi.PsiDocumentManager;
|
import com.intellij.psi.PsiDocumentManager;
|
||||||
import com.intellij.psi.PsiFile;
|
import com.intellij.psi.PsiFile;
|
||||||
|
@ -58,6 +54,8 @@ public class LSPGotoDefinitionAction extends ShowImplementationsAction {
|
||||||
super.actionPerformed(e);
|
super.actionPerformed(e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
manager.gotoDefinition(psiElement);
|
if (!manager.gotoDefinition(psiElement)) {
|
||||||
|
super.actionPerformed(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,14 +15,12 @@
|
||||||
*/
|
*/
|
||||||
package com.falsepattern.zigbrains.lsp.actions;
|
package com.falsepattern.zigbrains.lsp.actions;
|
||||||
|
|
||||||
|
import com.falsepattern.zigbrains.common.util.ApplicationUtil;
|
||||||
import com.falsepattern.zigbrains.lsp.IntellijLanguageClient;
|
import com.falsepattern.zigbrains.lsp.IntellijLanguageClient;
|
||||||
import com.falsepattern.zigbrains.lsp.requests.ReformatHandler;
|
import com.falsepattern.zigbrains.lsp.requests.ReformatHandler;
|
||||||
import com.falsepattern.zigbrains.lsp.utils.ApplicationUtils;
|
|
||||||
import com.intellij.codeInsight.actions.ReformatCodeAction;
|
import com.intellij.codeInsight.actions.ReformatCodeAction;
|
||||||
import com.intellij.lang.LanguageFormatting;
|
|
||||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||||
import com.intellij.openapi.application.ApplicationManager;
|
|
||||||
import com.intellij.openapi.diagnostic.Logger;
|
import com.intellij.openapi.diagnostic.Logger;
|
||||||
import com.intellij.openapi.editor.Editor;
|
import com.intellij.openapi.editor.Editor;
|
||||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||||
|
@ -51,7 +49,7 @@ public class LSPReformatAction extends ReformatCodeAction implements DumbAware {
|
||||||
super.actionPerformed(e);
|
super.actionPerformed(e);
|
||||||
return;
|
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 hasSelection, only reformat selection, not reformat the whole file
|
||||||
if (editor.getSelectionModel().hasSelection()) {
|
if (editor.getSelectionModel().hasSelection()) {
|
||||||
ReformatHandler.reformatSelection(editor);
|
ReformatHandler.reformatSelection(editor);
|
||||||
|
|
|
@ -22,7 +22,6 @@ import com.intellij.codeInsight.actions.LayoutCodeDialog;
|
||||||
import com.intellij.codeInsight.actions.LayoutCodeOptions;
|
import com.intellij.codeInsight.actions.LayoutCodeOptions;
|
||||||
import com.intellij.codeInsight.actions.ShowReformatFileDialog;
|
import com.intellij.codeInsight.actions.ShowReformatFileDialog;
|
||||||
import com.intellij.codeInsight.actions.TextRangeType;
|
import com.intellij.codeInsight.actions.TextRangeType;
|
||||||
import com.intellij.lang.LanguageFormatting;
|
|
||||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||||
import com.intellij.openapi.diagnostic.Logger;
|
import com.intellij.openapi.diagnostic.Logger;
|
||||||
|
|
|
@ -15,10 +15,10 @@
|
||||||
*/
|
*/
|
||||||
package com.falsepattern.zigbrains.lsp.client;
|
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.editor.EditorEventManagerBase;
|
||||||
import com.falsepattern.zigbrains.lsp.requests.WorkspaceEditHandler;
|
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.Notification;
|
||||||
import com.intellij.notification.NotificationAction;
|
import com.intellij.notification.NotificationAction;
|
||||||
import com.intellij.notification.NotificationGroup;
|
import com.intellij.notification.NotificationGroup;
|
||||||
|
@ -123,7 +123,7 @@ public class DefaultLanguageClient implements LanguageClient {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void publishDiagnostics(PublishDiagnosticsParams publishDiagnosticsParams) {
|
public void publishDiagnostics(PublishDiagnosticsParams publishDiagnosticsParams) {
|
||||||
String uri = FileUtils.sanitizeURI(publishDiagnosticsParams.getUri());
|
String uri = FileUtil.sanitizeURI(publishDiagnosticsParams.getUri());
|
||||||
List<Diagnostic> diagnostics = publishDiagnosticsParams.getDiagnostics();
|
List<Diagnostic> diagnostics = publishDiagnosticsParams.getDiagnostics();
|
||||||
EditorEventManagerBase.diagnostics(uri, diagnostics);
|
EditorEventManagerBase.diagnostics(uri, diagnostics);
|
||||||
}
|
}
|
||||||
|
@ -134,7 +134,7 @@ public class DefaultLanguageClient implements LanguageClient {
|
||||||
String message = messageParams.getMessage();
|
String message = messageParams.getMessage();
|
||||||
|
|
||||||
if (isModal) {
|
if (isModal) {
|
||||||
ApplicationUtils.invokeLater(() -> {
|
ApplicationUtil.invokeLater(() -> {
|
||||||
MessageType msgType = messageParams.getType();
|
MessageType msgType = messageParams.getType();
|
||||||
switch (msgType) {
|
switch (msgType) {
|
||||||
case Error:
|
case Error:
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package com.falsepattern.zigbrains.lsp.client.languageserver.serverdefinition;
|
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 com.intellij.openapi.diagnostic.Logger;
|
||||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
@ -24,7 +24,6 @@ import org.eclipse.lsp4j.InitializeParams;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.net.URI;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
@ -32,7 +31,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||||
/**
|
/**
|
||||||
* A trait representing a ServerDefinition
|
* A trait representing a ServerDefinition
|
||||||
*/
|
*/
|
||||||
public class LanguageServerDefinition {
|
public abstract class LanguageServerDefinition {
|
||||||
|
|
||||||
private static final Logger LOG = Logger.getInstance(LanguageServerDefinition.class);
|
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
|
* 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.
|
* 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
|
* @param workingDir The root directory
|
||||||
* @return The stream connection provider
|
* @return The stream connection provider
|
||||||
*/
|
*/
|
||||||
public StreamConnectionProvider createConnectionProvider(String workingDir) {
|
public abstract StreamConnectionProvider createConnectionProvider(String workingDir);
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ServerListener getServerListener() {
|
public ServerListener getServerListener() {
|
||||||
return ServerListener.DEFAULT;
|
return ServerListener.DEFAULT;
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package com.falsepattern.zigbrains.lsp.client.languageserver.serverdefinition;
|
package com.falsepattern.zigbrains.lsp.client.languageserver.serverdefinition;
|
||||||
|
|
||||||
import com.falsepattern.zigbrains.lsp.client.connection.ProcessStreamConnectionProvider;
|
import com.falsepattern.zigbrains.lspcommon.connection.ProcessStreamConnectionProvider;
|
||||||
import com.falsepattern.zigbrains.lsp.client.connection.StreamConnectionProvider;
|
import com.falsepattern.zigbrains.lspcommon.connection.StreamConnectionProvider;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package com.falsepattern.zigbrains.lsp.client.languageserver.serverdefinition;
|
package com.falsepattern.zigbrains.lsp.client.languageserver.serverdefinition;
|
||||||
|
|
||||||
import com.falsepattern.zigbrains.lsp.client.connection.ProcessStreamConnectionProvider;
|
import com.falsepattern.zigbrains.lspcommon.connection.ProcessStreamConnectionProvider;
|
||||||
import com.falsepattern.zigbrains.lsp.client.connection.StreamConnectionProvider;
|
import com.falsepattern.zigbrains.lspcommon.connection.StreamConnectionProvider;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package com.falsepattern.zigbrains.lsp.client.languageserver.wrapper;
|
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.IntellijLanguageClient;
|
||||||
import com.falsepattern.zigbrains.lsp.client.DefaultLanguageClient;
|
import com.falsepattern.zigbrains.lsp.client.DefaultLanguageClient;
|
||||||
import com.falsepattern.zigbrains.lsp.client.ServerWrapperBaseClientContext;
|
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.DefaultRequestManager;
|
||||||
import com.falsepattern.zigbrains.lsp.client.languageserver.requestmanager.RequestManager;
|
import com.falsepattern.zigbrains.lsp.client.languageserver.requestmanager.RequestManager;
|
||||||
import com.falsepattern.zigbrains.lsp.client.languageserver.serverdefinition.LanguageServerDefinition;
|
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.EditorEventManager;
|
||||||
import com.falsepattern.zigbrains.lsp.editor.EditorEventManagerBase;
|
import com.falsepattern.zigbrains.lsp.editor.EditorEventManagerBase;
|
||||||
import com.falsepattern.zigbrains.lsp.extensions.LSPExtensionManager;
|
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.requests.Timeouts;
|
||||||
import com.falsepattern.zigbrains.lsp.statusbar.LSPServerStatusWidget;
|
import com.falsepattern.zigbrains.lsp.statusbar.LSPServerStatusWidget;
|
||||||
import com.falsepattern.zigbrains.lsp.statusbar.LSPServerStatusWidgetFactory;
|
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.FileUtils;
|
||||||
import com.falsepattern.zigbrains.lsp.utils.LSPException;
|
import com.falsepattern.zigbrains.lsp.utils.LSPException;
|
||||||
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
|
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.FileEditor;
|
||||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
|
import com.intellij.openapi.project.ProjectUtil;
|
||||||
import com.intellij.openapi.ui.MessageType;
|
import com.intellij.openapi.ui.MessageType;
|
||||||
import com.intellij.openapi.ui.Messages;
|
import com.intellij.openapi.ui.Messages;
|
||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
|
@ -51,6 +52,7 @@ import com.intellij.psi.PsiDocumentManager;
|
||||||
import com.intellij.psi.PsiFile;
|
import com.intellij.psi.PsiFile;
|
||||||
import com.intellij.remoteServer.util.CloudNotifier;
|
import com.intellij.remoteServer.util.CloudNotifier;
|
||||||
import com.intellij.util.PlatformIcons;
|
import com.intellij.util.PlatformIcons;
|
||||||
|
import lombok.val;
|
||||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.eclipse.lsp4j.ClientCapabilities;
|
import org.eclipse.lsp4j.ClientCapabilities;
|
||||||
|
@ -104,6 +106,7 @@ import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
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)
|
* The implementation of a LanguageServerWrapper (specific to a serverDefinition and a project)
|
||||||
*/
|
*/
|
||||||
public class LanguageServerWrapper {
|
public class LanguageServerWrapper {
|
||||||
|
public final LanguageServerDefinition serverDefinition;
|
||||||
public LanguageServerDefinition serverDefinition;
|
|
||||||
private final LSPExtensionManager extManager;
|
private final LSPExtensionManager extManager;
|
||||||
private final Project project;
|
private final Project project;
|
||||||
private final HashSet<Editor> toConnect = new HashSet<>();
|
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<String> urisUnderLspControl = new HashSet<>();
|
||||||
private final HashSet<Editor> connectedEditors = new HashSet<>();
|
private final HashSet<Editor> connectedEditors = new HashSet<>();
|
||||||
private final Map<String, Set<EditorEventManager>> uriToEditorManagers = new HashMap<>();
|
private final Map<String, Set<EditorEventManager>> uriToEditorManagers = new HashMap<>();
|
||||||
|
@ -167,7 +170,12 @@ public class LanguageServerWrapper {
|
||||||
this.project = project;
|
this.project = project;
|
||||||
// We need to keep the project rootPath in addition to the project instance, since we cannot get the 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.
|
// 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;
|
this.extManager = extManager;
|
||||||
projectToLanguageServerWrapper.put(project, this);
|
projectToLanguageServerWrapper.put(project, this);
|
||||||
}
|
}
|
||||||
|
@ -182,7 +190,7 @@ public class LanguageServerWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LanguageServerWrapper forVirtualFile(VirtualFile file, Project project) {
|
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;
|
return serverDefinition;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getProjectRootPath() {
|
public Path getProjectRootPath() {
|
||||||
return projectRootPath;
|
return projectRootPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,7 +245,7 @@ public class LanguageServerWrapper {
|
||||||
String msg = String.format("%s \n is not initialized after %d seconds",
|
String msg = String.format("%s \n is not initialized after %d seconds",
|
||||||
serverDefinition.toString(), Timeout.getTimeout(Timeouts.INIT) / 1000);
|
serverDefinition.toString(), Timeout.getTimeout(Timeouts.INIT) / 1000);
|
||||||
LOG.warn(msg, e);
|
LOG.warn(msg, e);
|
||||||
ApplicationUtils.invokeLater(() -> {
|
ApplicationUtil.invokeLater(() -> {
|
||||||
if (!alreadyShownTimeout) {
|
if (!alreadyShownTimeout) {
|
||||||
notifier.showMessage(msg, MessageType.WARNING);
|
notifier.showMessage(msg, MessageType.WARNING);
|
||||||
alreadyShownTimeout = true;
|
alreadyShownTimeout = true;
|
||||||
|
@ -280,7 +288,7 @@ public class LanguageServerWrapper {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
VirtualFile currentOpenFile = selectedEditor.getFile();
|
VirtualFile currentOpenFile = selectedEditor.getFile();
|
||||||
VirtualFile requestedFile = FileUtils.virtualFileFromURI(uri);
|
VirtualFile requestedFile = FileUtil.virtualFileFromURI(uri);
|
||||||
if (currentOpenFile == null || requestedFile == null) {
|
if (currentOpenFile == null || requestedFile == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -396,7 +404,7 @@ public class LanguageServerWrapper {
|
||||||
}
|
}
|
||||||
// Triggers annotators since this is the first editor which starts the LS
|
// Triggers annotators since this is the first editor which starts the LS
|
||||||
// and annotators are executed before LS is bootstrap to provide diagnostics.
|
// and annotators are executed before LS is bootstrap to provide diagnostics.
|
||||||
ApplicationUtils.computableReadAction(() -> {
|
ApplicationUtil.computableReadAction(() -> {
|
||||||
PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
|
PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
|
||||||
if (psiFile != null) {
|
if (psiFile != null) {
|
||||||
DaemonCodeAnalyzer.getInstance(project).restart(psiFile);
|
DaemonCodeAnalyzer.getInstance(project).restart(psiFile);
|
||||||
|
@ -449,7 +457,7 @@ public class LanguageServerWrapper {
|
||||||
launcherFuture.cancel(true);
|
launcherFuture.cancel(true);
|
||||||
}
|
}
|
||||||
if (serverDefinition != null) {
|
if (serverDefinition != null) {
|
||||||
serverDefinition.stop(projectRootPath);
|
serverDefinition.stop(projectRootPath != null ? projectRootPath.toString() : null);
|
||||||
}
|
}
|
||||||
for (Editor ed : new HashSet<>(connectedEditors)) {
|
for (Editor ed : new HashSet<>(connectedEditors)) {
|
||||||
disconnect(ed);
|
disconnect(ed);
|
||||||
|
@ -496,7 +504,7 @@ public class LanguageServerWrapper {
|
||||||
if (status == STOPPED && !alreadyShownCrash && !alreadyShownTimeout) {
|
if (status == STOPPED && !alreadyShownCrash && !alreadyShownTimeout) {
|
||||||
setStatus(STARTING);
|
setStatus(STARTING);
|
||||||
try {
|
try {
|
||||||
Pair<InputStream, OutputStream> streams = serverDefinition.start(projectRootPath);
|
Pair<InputStream, OutputStream> streams = serverDefinition.start(projectRootPath != null ? projectRootPath.toString() : null);
|
||||||
InputStream inputStream = streams.getKey();
|
InputStream inputStream = streams.getKey();
|
||||||
OutputStream outputStream = streams.getValue();
|
OutputStream outputStream = streams.getValue();
|
||||||
InitializeParams initParams = getInitParams();
|
InitializeParams initParams = getInitParams();
|
||||||
|
@ -540,7 +548,7 @@ public class LanguageServerWrapper {
|
||||||
});
|
});
|
||||||
} catch (LSPException | IOException | URISyntaxException e) {
|
} catch (LSPException | IOException | URISyntaxException e) {
|
||||||
LOG.warn(e);
|
LOG.warn(e);
|
||||||
ApplicationUtils.invokeLater(() ->
|
ApplicationUtil.invokeLater(() ->
|
||||||
notifier.showMessage(String.format("Can't start server due to %s", e.getMessage()),
|
notifier.showMessage(String.format("Can't start server due to %s", e.getMessage()),
|
||||||
MessageType.WARNING));
|
MessageType.WARNING));
|
||||||
removeServerWrapper();
|
removeServerWrapper();
|
||||||
|
@ -550,7 +558,7 @@ public class LanguageServerWrapper {
|
||||||
|
|
||||||
private InitializeParams getInitParams() throws URISyntaxException {
|
private InitializeParams getInitParams() throws URISyntaxException {
|
||||||
InitializeParams initParams = new InitializeParams();
|
InitializeParams initParams = new InitializeParams();
|
||||||
String projectRootUri = FileUtils.pathToUri(projectRootPath);
|
String projectRootUri = FileUtil.pathToUri(projectRootPath);
|
||||||
WorkspaceFolder workspaceFolder = new WorkspaceFolder(projectRootUri, this.project.getName());
|
WorkspaceFolder workspaceFolder = new WorkspaceFolder(projectRootUri, this.project.getName());
|
||||||
initParams.setWorkspaceFolders(Collections.singletonList(workspaceFolder));
|
initParams.setWorkspaceFolders(Collections.singletonList(workspaceFolder));
|
||||||
|
|
||||||
|
@ -591,8 +599,6 @@ public class LanguageServerWrapper {
|
||||||
new ClientCapabilities(workspaceClientCapabilities, textDocumentClientCapabilities, null));
|
new ClientCapabilities(workspaceClientCapabilities, textDocumentClientCapabilities, null));
|
||||||
initParams.setClientInfo(new ClientInfo(ApplicationInfo.getInstance().getVersionName(), ApplicationInfo.getInstance().getFullVersion()));
|
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);
|
serverDefinition.customizeInitializeParams(initParams);
|
||||||
return initParams;
|
return initParams;
|
||||||
}
|
}
|
||||||
|
@ -639,7 +645,7 @@ public class LanguageServerWrapper {
|
||||||
if (crashCount <= 3) {
|
if (crashCount <= 3) {
|
||||||
reconnect();
|
reconnect();
|
||||||
} else {
|
} else {
|
||||||
ApplicationUtils.invokeLater(() -> {
|
ApplicationUtil.invokeLater(() -> {
|
||||||
if (alreadyShownCrash) {
|
if (alreadyShownCrash) {
|
||||||
reconnect();
|
reconnect();
|
||||||
} else {
|
} else {
|
||||||
|
@ -679,7 +685,7 @@ public class LanguageServerWrapper {
|
||||||
List<String> connected = new ArrayList<>();
|
List<String> connected = new ArrayList<>();
|
||||||
urisUnderLspControl.forEach(s -> {
|
urisUnderLspControl.forEach(s -> {
|
||||||
try {
|
try {
|
||||||
connected.add(new URI(FileUtils.sanitizeURI(s)).toString());
|
connected.add(new URI(FileUtil.sanitizeURI(s)).toString());
|
||||||
} catch (URISyntaxException e) {
|
} catch (URISyntaxException e) {
|
||||||
LOG.warn(e);
|
LOG.warn(e);
|
||||||
}
|
}
|
||||||
|
@ -733,7 +739,7 @@ public class LanguageServerWrapper {
|
||||||
* @param projectUri The project root uri
|
* @param projectUri The project root uri
|
||||||
*/
|
*/
|
||||||
public void disconnect(String uri, String projectUri) {
|
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);
|
Set<EditorEventManager> managers = uriToEditorManagers.get(uri);
|
||||||
if (managers == null) {
|
if (managers == null) {
|
||||||
|
@ -752,7 +758,7 @@ public class LanguageServerWrapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
urisUnderLspControl.remove(uri);
|
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()) {
|
if (connectedEditors.isEmpty()) {
|
||||||
stop(true);
|
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.
|
* Reset language server wrapper state so it can be started again if it was failed earlier.
|
||||||
*/
|
*/
|
||||||
public void restart() {
|
public void restart() {
|
||||||
ApplicationUtils.pool(() -> {
|
ApplicationUtil.pool(() -> {
|
||||||
if (isRestartable()) {
|
if (isRestartable()) {
|
||||||
alreadyShownCrash = false;
|
alreadyShownCrash = false;
|
||||||
alreadyShownTimeout = false;
|
alreadyShownTimeout = false;
|
||||||
|
|
|
@ -16,13 +16,14 @@
|
||||||
|
|
||||||
package com.falsepattern.zigbrains.lsp.contributors;
|
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.editor.EditorEventManagerBase;
|
||||||
import com.falsepattern.zigbrains.lsp.requests.HoverHandler;
|
import com.falsepattern.zigbrains.lsp.requests.HoverHandler;
|
||||||
import com.falsepattern.zigbrains.lsp.requests.Timeout;
|
import com.falsepattern.zigbrains.lsp.requests.Timeout;
|
||||||
import com.falsepattern.zigbrains.lsp.requests.Timeouts;
|
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.DocumentUtils;
|
||||||
import com.falsepattern.zigbrains.lsp.utils.FileUtils;
|
import com.falsepattern.zigbrains.lsp.utils.FileUtils;
|
||||||
|
import com.intellij.markdown.utils.doc.DocMarkdownToHtmlConverter;
|
||||||
import com.intellij.model.Pointer;
|
import com.intellij.model.Pointer;
|
||||||
import com.intellij.openapi.diagnostic.Logger;
|
import com.intellij.openapi.diagnostic.Logger;
|
||||||
import com.intellij.openapi.util.TextRange;
|
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.PsiFile;
|
||||||
import com.intellij.psi.SmartPointerManager;
|
import com.intellij.psi.SmartPointerManager;
|
||||||
import com.intellij.psi.SmartPsiFileRange;
|
import com.intellij.psi.SmartPsiFileRange;
|
||||||
|
import lombok.val;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.eclipse.lsp4j.HoverParams;
|
import org.eclipse.lsp4j.HoverParams;
|
||||||
import org.eclipse.lsp4j.jsonrpc.JsonRpcException;
|
import org.eclipse.lsp4j.jsonrpc.JsonRpcException;
|
||||||
|
@ -44,7 +46,6 @@ import java.util.List;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class LSPDocumentationTargetProvider implements DocumentationTargetProvider {
|
public class LSPDocumentationTargetProvider implements DocumentationTargetProvider {
|
||||||
@Override
|
@Override
|
||||||
|
@ -86,7 +87,7 @@ public class LSPDocumentationTargetProvider implements DocumentationTargetProvid
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
var caretPos = editor.offsetToLogicalPosition(offset);
|
var caretPos = editor.offsetToLogicalPosition(offset);
|
||||||
var serverPos = ApplicationUtils.computableReadAction(() -> DocumentUtils.logicalToLSPPos(caretPos, editor));
|
var serverPos = ApplicationUtil.computableReadAction(() -> DocumentUtils.logicalToLSPPos(caretPos, editor));
|
||||||
return DocumentationResult.asyncDocumentation(() -> {
|
return DocumentationResult.asyncDocumentation(() -> {
|
||||||
var identifier = manager.getIdentifier();
|
var identifier = manager.getIdentifier();
|
||||||
var request = wrapper.getRequestManager().hover(new HoverParams(identifier, serverPos));
|
var request = wrapper.getRequestManager().hover(new HoverParams(identifier, serverPos));
|
||||||
|
@ -102,13 +103,15 @@ public class LSPDocumentationTargetProvider implements DocumentationTargetProvid
|
||||||
return null;
|
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)) {
|
if (StringUtils.isEmpty(string)) {
|
||||||
LOG.warn(String.format("Hover string returned is empty for file %s and pos (%d;%d)",
|
LOG.warn(String.format("Hover string returned is empty for file %s and pos (%d;%d)",
|
||||||
identifier.getUri(), serverPos.getLine(), serverPos.getCharacter()));
|
identifier.getUri(), serverPos.getLine(), serverPos.getCharacter()));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return DocumentationResult.documentation(string.lines().collect(Collectors.joining("<br>\n")));
|
return DocumentationResult.documentation(string);
|
||||||
} catch (TimeoutException e) {
|
} catch (TimeoutException e) {
|
||||||
LOG.warn(e);
|
LOG.warn(e);
|
||||||
wrapper.notifyFailure(Timeouts.HOVER);
|
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.InlineInlayPosition;
|
||||||
import com.intellij.codeInsight.hints.declarative.OwnBypassCollector;
|
import com.intellij.codeInsight.hints.declarative.OwnBypassCollector;
|
||||||
import com.intellij.codeInsight.hints.declarative.impl.DeclarativeInlayHintsPassFactory;
|
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.diagnostic.Logger;
|
||||||
import com.intellij.openapi.editor.Editor;
|
import com.intellij.openapi.editor.Editor;
|
||||||
import com.intellij.psi.PsiFile;
|
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.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
@ -92,12 +91,10 @@ public class LSPInlayHintProvider implements InlayHintsProvider {
|
||||||
tooltipText = switch (markup.getKind()) {
|
tooltipText = switch (markup.getKind()) {
|
||||||
case "markdown" -> {
|
case "markdown" -> {
|
||||||
var markedContent = markup.getValue();
|
var markedContent = markup.getValue();
|
||||||
if (markedContent.isEmpty()) {
|
if (markedContent.isBlank()) {
|
||||||
yield "";
|
yield "";
|
||||||
}
|
}
|
||||||
Parser parser = Parser.builder().build();
|
yield DocMarkdownToHtmlConverter.convert(manager.getProject(), markedContent);
|
||||||
HtmlRenderer renderer = HtmlRenderer.builder().build();
|
|
||||||
yield "<html>" + renderer.render(parser.parse(markedContent)) + "</html>";
|
|
||||||
}
|
}
|
||||||
default -> markup.getValue();
|
default -> markup.getValue();
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package com.falsepattern.zigbrains.lsp.contributors.annotator;
|
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.IntellijLanguageClient;
|
||||||
import com.falsepattern.zigbrains.lsp.client.languageserver.ServerStatus;
|
import com.falsepattern.zigbrains.lsp.client.languageserver.ServerStatus;
|
||||||
import com.falsepattern.zigbrains.lsp.client.languageserver.wrapper.LanguageServerWrapper;
|
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.editor.Editor;
|
||||||
import com.intellij.openapi.util.TextRange;
|
import com.intellij.openapi.util.TextRange;
|
||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
|
import com.intellij.psi.PsiDocumentManager;
|
||||||
import com.intellij.psi.PsiFile;
|
import com.intellij.psi.PsiFile;
|
||||||
import com.intellij.util.SmartList;
|
import com.intellij.util.SmartList;
|
||||||
|
import lombok.val;
|
||||||
import org.eclipse.lsp4j.Diagnostic;
|
import org.eclipse.lsp4j.Diagnostic;
|
||||||
import org.eclipse.lsp4j.DiagnosticSeverity;
|
import org.eclipse.lsp4j.DiagnosticSeverity;
|
||||||
import org.eclipse.lsp4j.DiagnosticTag;
|
import org.eclipse.lsp4j.DiagnosticTag;
|
||||||
|
@ -103,7 +106,7 @@ public class LSPAnnotator extends ExternalAnnotator<Object, Object> {
|
||||||
|
|
||||||
VirtualFile virtualFile = file.getVirtualFile();
|
VirtualFile virtualFile = file.getVirtualFile();
|
||||||
if (FileUtils.isFileSupported(virtualFile) && IntellijLanguageClient.isExtensionSupported(virtualFile)) {
|
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..
|
// 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);
|
EditorEventManager eventManager = EditorEventManagerBase.forUri(uri);
|
||||||
if (eventManager == null) {
|
if (eventManager == null) {
|
||||||
|
@ -161,14 +164,17 @@ public class LSPAnnotator extends ExternalAnnotator<Object, Object> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@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 start = DocumentUtils.LSPPosToOffset(editor, diagnostic.getRange().getStart());
|
||||||
final int end = DocumentUtils.LSPPosToOffset(editor, diagnostic.getRange().getEnd());
|
final int end = DocumentUtils.LSPPosToOffset(editor, diagnostic.getRange().getEnd());
|
||||||
if (start >= end) {
|
if (start > end) {
|
||||||
return null;
|
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())
|
return holder.newAnnotation(lspToIntellijAnnotationsMap.get(diagnostic.getSeverity()), diagnostic.getMessage())
|
||||||
.range(range);
|
.range(range);
|
||||||
}
|
}
|
||||||
|
@ -176,18 +182,47 @@ public class LSPAnnotator extends ExternalAnnotator<Object, Object> {
|
||||||
private void createAnnotations(AnnotationHolder holder, EditorEventManager eventManager) {
|
private void createAnnotations(AnnotationHolder holder, EditorEventManager eventManager) {
|
||||||
final List<Diagnostic> diagnostics = eventManager.getDiagnostics();
|
final List<Diagnostic> diagnostics = eventManager.getDiagnostics();
|
||||||
final Editor editor = eventManager.editor;
|
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<>();
|
List<Annotation> annotations = new ArrayList<>();
|
||||||
diagnostics.forEach(d -> {
|
diagnostics.forEach(d -> {
|
||||||
var annotation = createAnnotation(editor, holder, d);
|
var range = getTextRange(editor, d);
|
||||||
if (annotation != null) {
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var annotation = createAnnotation(range, holder, d);
|
||||||
if (d.getTags() != null && d.getTags().contains(DiagnosticTag.Deprecated)) {
|
if (d.getTags() != null && d.getTags().contains(DiagnosticTag.Deprecated)) {
|
||||||
annotation = annotation.highlightType(ProblemHighlightType.LIKE_DEPRECATED);
|
annotation = annotation.highlightType(ProblemHighlightType.LIKE_DEPRECATED);
|
||||||
}
|
}
|
||||||
annotation.create();
|
annotation.create();
|
||||||
var theList = (SmartList<Annotation>) holder;
|
var theList = (SmartList<Annotation>) holder;
|
||||||
annotations.add(theList.get(theList.size() - 1));
|
annotations.add(theList.get(theList.size() - 1));
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
eventManager.setAnnotations(annotations);
|
eventManager.setAnnotations(annotations);
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package com.falsepattern.zigbrains.lsp.contributors.psi;
|
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.falsepattern.zigbrains.lsp.utils.FileUtils;
|
||||||
import com.intellij.lang.ASTNode;
|
import com.intellij.lang.ASTNode;
|
||||||
import com.intellij.lang.Language;
|
import com.intellij.lang.Language;
|
||||||
|
@ -700,7 +700,7 @@ public class LSPPsiElement extends PsiElementBase implements PsiNameIdentifierOw
|
||||||
if (editor == null) {
|
if (editor == null) {
|
||||||
OpenFileDescriptor descriptor = new OpenFileDescriptor(getProject(), getContainingFile().getVirtualFile(),
|
OpenFileDescriptor descriptor = new OpenFileDescriptor(getProject(), getContainingFile().getVirtualFile(),
|
||||||
getTextOffset());
|
getTextOffset());
|
||||||
ApplicationUtils.invokeLater(() -> ApplicationUtils
|
ApplicationUtil.invokeLater(() -> ApplicationUtil
|
||||||
.writeAction(() -> FileEditorManager.getInstance(getProject()).openTextEditor(descriptor, false)));
|
.writeAction(() -> FileEditorManager.getInstance(getProject()).openTextEditor(descriptor, false)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package com.falsepattern.zigbrains.lsp.contributors.symbol;
|
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.IntellijLanguageClient;
|
||||||
import com.falsepattern.zigbrains.lsp.client.languageserver.ServerStatus;
|
import com.falsepattern.zigbrains.lsp.client.languageserver.ServerStatus;
|
||||||
import com.falsepattern.zigbrains.lsp.client.languageserver.requestmanager.RequestManager;
|
import com.falsepattern.zigbrains.lsp.client.languageserver.requestmanager.RequestManager;
|
||||||
|
@ -70,7 +71,7 @@ public class WorkspaceSymbolProvider {
|
||||||
final SymbolInformation information = (result.getSymbolInformation() != null) ?
|
final SymbolInformation information = (result.getSymbolInformation() != null) ?
|
||||||
result.getSymbolInformation() : from(result.getWorkspaceSymbol());
|
result.getSymbolInformation() : from(result.getWorkspaceSymbol());
|
||||||
final Location location = information.getLocation();
|
final Location location = information.getLocation();
|
||||||
final VirtualFile file = FileUtils.URIToVFS(location.getUri());
|
final VirtualFile file = FileUtil.virtualFileFromURI(location.getUri());
|
||||||
|
|
||||||
if (file != null) {
|
if (file != null) {
|
||||||
final LSPIconProvider iconProviderFor = GUIUtils.getIconProviderFor(result.getDefinition());
|
final LSPIconProvider iconProviderFor = GUIUtils.getIconProviderFor(result.getDefinition());
|
||||||
|
|
|
@ -15,23 +15,18 @@
|
||||||
*/
|
*/
|
||||||
package com.falsepattern.zigbrains.lsp.editor;
|
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.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.falsepattern.zigbrains.lsp.utils.FileUtils;
|
||||||
import com.intellij.openapi.diagnostic.Logger;
|
import com.intellij.openapi.diagnostic.Logger;
|
||||||
import com.intellij.openapi.editor.Document;
|
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.DocumentEvent;
|
||||||
import com.intellij.openapi.editor.event.DocumentListener;
|
import com.intellij.openapi.editor.event.DocumentListener;
|
||||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||||
import com.intellij.openapi.util.io.FileUtilRt;
|
import com.intellij.openapi.util.io.FileUtilRt;
|
||||||
import com.intellij.openapi.util.text.StringUtil;
|
|
||||||
import org.eclipse.lsp4j.DidChangeTextDocumentParams;
|
import org.eclipse.lsp4j.DidChangeTextDocumentParams;
|
||||||
import org.eclipse.lsp4j.DidCloseTextDocumentParams;
|
import org.eclipse.lsp4j.DidCloseTextDocumentParams;
|
||||||
import org.eclipse.lsp4j.DidOpenTextDocumentParams;
|
import org.eclipse.lsp4j.DidOpenTextDocumentParams;
|
||||||
import org.eclipse.lsp4j.Position;
|
|
||||||
import org.eclipse.lsp4j.Range;
|
|
||||||
import org.eclipse.lsp4j.TextDocumentContentChangeEvent;
|
import org.eclipse.lsp4j.TextDocumentContentChangeEvent;
|
||||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||||
import org.eclipse.lsp4j.TextDocumentItem;
|
import org.eclipse.lsp4j.TextDocumentItem;
|
||||||
|
@ -78,49 +73,50 @@ public class DocumentEventManager {
|
||||||
DidChangeTextDocumentParams changesParams = new DidChangeTextDocumentParams(new VersionedTextDocumentIdentifier(),
|
DidChangeTextDocumentParams changesParams = new DidChangeTextDocumentParams(new VersionedTextDocumentIdentifier(),
|
||||||
Collections.singletonList(new TextDocumentContentChangeEvent()));
|
Collections.singletonList(new TextDocumentContentChangeEvent()));
|
||||||
changesParams.getTextDocument().setUri(identifier.getUri());
|
changesParams.getTextDocument().setUri(identifier.getUri());
|
||||||
|
|
||||||
|
|
||||||
changesParams.getTextDocument().setVersion(++version);
|
changesParams.getTextDocument().setVersion(++version);
|
||||||
|
|
||||||
if (syncKind == TextDocumentSyncKind.Incremental) {
|
// TODO this incremental update logic is kinda broken, investigate later...
|
||||||
TextDocumentContentChangeEvent changeEvent = changesParams.getContentChanges().get(0);
|
// if (syncKind == TextDocumentSyncKind.Incremental) {
|
||||||
CharSequence newText = event.getNewFragment();
|
// TextDocumentContentChangeEvent changeEvent = changesParams.getContentChanges().get(0);
|
||||||
int offset = event.getOffset();
|
// CharSequence newText = event.getNewFragment();
|
||||||
int newTextLength = event.getNewLength();
|
// int offset = event.getOffset();
|
||||||
|
// int newTextLength = event.getNewLength();
|
||||||
EditorEventManager editorEventManager = EditorEventManagerBase.forUri(FileUtils.documentToUri(document));
|
//
|
||||||
if (editorEventManager == null) {
|
// EditorEventManager editorEventManager = EditorEventManagerBase.forUri(FileUtils.documentToUri(document));
|
||||||
LOG.warn("no editor associated with document");
|
// if (editorEventManager == null) {
|
||||||
return;
|
// LOG.warn("no editor associated with document");
|
||||||
}
|
// return;
|
||||||
Editor editor = editorEventManager.editor;
|
// }
|
||||||
Position lspPosition = DocumentUtils.offsetToLSPPos(editor, offset);
|
// Editor editor = editorEventManager.editor;
|
||||||
if (lspPosition == null) {
|
// Position lspPosition = DocumentUtils.offsetToLSPPos(editor, offset);
|
||||||
return;
|
// if (lspPosition == null) {
|
||||||
}
|
// return;
|
||||||
int startLine = lspPosition.getLine();
|
// }
|
||||||
int startColumn = lspPosition.getCharacter();
|
// int startLine = lspPosition.getLine();
|
||||||
CharSequence oldText = event.getOldFragment();
|
// 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 text was deleted/replaced, calculate the end position of inserted/deleted text
|
||||||
if (oldText.length() > 0) {
|
// int endLine, endColumn;
|
||||||
endLine = startLine + StringUtil.countNewLines(oldText);
|
// if (oldText.length() > 0) {
|
||||||
String content = oldText.toString();
|
// endLine = startLine + StringUtil.countNewLines(oldText);
|
||||||
String[] oldLines = content.split("\n");
|
// String content = oldText.toString();
|
||||||
int oldTextLength = oldLines.length == 0 ? 0 : oldLines[oldLines.length - 1].length();
|
// String[] oldLines = content.split("\n");
|
||||||
endColumn = content.endsWith("\n") ? 0 : oldLines.length == 1 ? startColumn + oldTextLength : oldTextLength;
|
// int oldTextLength = oldLines.length == 0 ? 0 : oldLines[oldLines.length - 1].length();
|
||||||
} else { //if insert or no text change, the end position is the same
|
// endColumn = content.endsWith("\n") ? 0 : oldLines.length == 1 ? startColumn + oldTextLength : oldTextLength;
|
||||||
endLine = startLine;
|
// } else { //if insert or no text change, the end position is the same
|
||||||
endColumn = startColumn;
|
// endLine = startLine;
|
||||||
}
|
// endColumn = startColumn;
|
||||||
Range range = new Range(new Position(startLine, startColumn), new Position(endLine, endColumn));
|
// }
|
||||||
changeEvent.setRange(range);
|
// Range range = new Range(new Position(startLine, startColumn), new Position(endLine, endColumn));
|
||||||
changeEvent.setText(newText.toString());
|
// changeEvent.setRange(range);
|
||||||
} else if (syncKind == TextDocumentSyncKind.Full) {
|
// changeEvent.setText(newText.toString());
|
||||||
|
// } else if (syncKind == TextDocumentSyncKind.Full) {
|
||||||
|
if (syncKind != TextDocumentSyncKind.None) {
|
||||||
changesParams.getContentChanges().get(0).setText(document.getText());
|
changesParams.getContentChanges().get(0).setText(document.getText());
|
||||||
}
|
}
|
||||||
ApplicationUtils.pool(() -> wrapper.getRequestManager().didChange(changesParams));
|
// }
|
||||||
|
ApplicationUtil.pool(() -> wrapper.getRequestManager().didChange(changesParams));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void documentOpened() {
|
public void documentOpened() {
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package com.falsepattern.zigbrains.lsp.editor;
|
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.actions.LSPReferencesAction;
|
||||||
import com.falsepattern.zigbrains.lsp.client.languageserver.ServerOptions;
|
import com.falsepattern.zigbrains.lsp.client.languageserver.ServerOptions;
|
||||||
import com.falsepattern.zigbrains.lsp.client.languageserver.requestmanager.RequestManager;
|
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.psi.LSPPsiElement;
|
||||||
import com.falsepattern.zigbrains.lsp.contributors.rename.LSPRenameProcessor;
|
import com.falsepattern.zigbrains.lsp.contributors.rename.LSPRenameProcessor;
|
||||||
import com.falsepattern.zigbrains.lsp.listeners.LSPCaretListenerImpl;
|
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.Timeout;
|
||||||
import com.falsepattern.zigbrains.lsp.requests.Timeouts;
|
import com.falsepattern.zigbrains.lsp.requests.Timeouts;
|
||||||
import com.falsepattern.zigbrains.lsp.requests.WorkspaceEditHandler;
|
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.DocumentUtils;
|
||||||
import com.falsepattern.zigbrains.lsp.utils.FileUtils;
|
import com.falsepattern.zigbrains.lsp.utils.FileUtils;
|
||||||
import com.falsepattern.zigbrains.lsp.utils.GUIUtils;
|
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.LogicalPosition;
|
||||||
import com.intellij.openapi.editor.ScrollType;
|
import com.intellij.openapi.editor.ScrollType;
|
||||||
import com.intellij.openapi.editor.SelectionModel;
|
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.DocumentEvent;
|
||||||
import com.intellij.openapi.editor.event.DocumentListener;
|
import com.intellij.openapi.editor.event.DocumentListener;
|
||||||
import com.intellij.openapi.editor.event.EditorMouseEvent;
|
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.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.FileDocumentManager;
|
||||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||||
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
|
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.util.TextRange;
|
||||||
import com.intellij.openapi.vfs.VfsUtil;
|
import com.intellij.openapi.vfs.VfsUtil;
|
||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
import com.intellij.openapi.vfs.VirtualFileManager;
|
|
||||||
import com.intellij.psi.PsiDocumentManager;
|
import com.intellij.psi.PsiDocumentManager;
|
||||||
import com.intellij.psi.PsiElement;
|
import com.intellij.psi.PsiElement;
|
||||||
import com.intellij.psi.PsiFile;
|
import com.intellij.psi.PsiFile;
|
||||||
|
@ -96,8 +90,6 @@ import org.eclipse.lsp4j.DocumentFormattingParams;
|
||||||
import org.eclipse.lsp4j.DocumentRangeFormattingParams;
|
import org.eclipse.lsp4j.DocumentRangeFormattingParams;
|
||||||
import org.eclipse.lsp4j.ExecuteCommandParams;
|
import org.eclipse.lsp4j.ExecuteCommandParams;
|
||||||
import org.eclipse.lsp4j.FormattingOptions;
|
import org.eclipse.lsp4j.FormattingOptions;
|
||||||
import org.eclipse.lsp4j.Hover;
|
|
||||||
import org.eclipse.lsp4j.HoverParams;
|
|
||||||
import org.eclipse.lsp4j.InlayHint;
|
import org.eclipse.lsp4j.InlayHint;
|
||||||
import org.eclipse.lsp4j.InlayHintParams;
|
import org.eclipse.lsp4j.InlayHintParams;
|
||||||
import org.eclipse.lsp4j.InsertReplaceEdit;
|
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.jsonrpc.messages.Tuple;
|
||||||
import org.eclipse.lsp4j.services.TextDocumentService;
|
import org.eclipse.lsp4j.services.TextDocumentService;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import java.awt.Point;
|
import java.awt.Point;
|
||||||
|
@ -144,15 +135,14 @@ import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import static com.falsepattern.zigbrains.lsp.utils.ApplicationUtils.computableReadAction;
|
import static com.falsepattern.zigbrains.common.util.ApplicationUtil.computableReadAction;
|
||||||
import static com.falsepattern.zigbrains.lsp.utils.ApplicationUtils.computableWriteAction;
|
import static com.falsepattern.zigbrains.common.util.ApplicationUtil.computableWriteAction;
|
||||||
import static com.falsepattern.zigbrains.lsp.utils.ApplicationUtils.invokeLater;
|
import static com.falsepattern.zigbrains.common.util.ApplicationUtil.invokeLater;
|
||||||
import static com.falsepattern.zigbrains.lsp.utils.ApplicationUtils.pool;
|
import static com.falsepattern.zigbrains.common.util.ApplicationUtil.pool;
|
||||||
import static com.falsepattern.zigbrains.lsp.utils.ApplicationUtils.writeAction;
|
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.DocumentUtils.toEither;
|
||||||
import static com.falsepattern.zigbrains.lsp.utils.GUIUtils.createAndShowEditorHint;
|
import static com.falsepattern.zigbrains.lsp.utils.GUIUtils.createAndShowEditorHint;
|
||||||
|
|
||||||
|
@ -370,8 +360,8 @@ public class EditorEventManager {
|
||||||
res.forEach(l -> {
|
res.forEach(l -> {
|
||||||
Position start = l.getRange().getStart();
|
Position start = l.getRange().getStart();
|
||||||
Position end = l.getRange().getEnd();
|
Position end = l.getRange().getEnd();
|
||||||
String uri = FileUtils.sanitizeURI(l.getUri());
|
String uri = FileUtil.sanitizeURI(l.getUri());
|
||||||
VirtualFile file = FileUtils.virtualFileFromURI(uri);
|
VirtualFile file = FileUtil.virtualFileFromURI(uri);
|
||||||
if (fast) {
|
if (fast) {
|
||||||
if (file == null)
|
if (file == null)
|
||||||
return;
|
return;
|
||||||
|
@ -1228,7 +1218,7 @@ public class EditorEventManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<InlayHint> inlayHint() {
|
public List<InlayHint> inlayHint() {
|
||||||
var range = ApplicationUtils.computableReadAction(() -> {
|
var range = ApplicationUtil.computableReadAction(() -> {
|
||||||
var start = DocumentUtils.offsetToLSPPos(editor, 0);
|
var start = DocumentUtils.offsetToLSPPos(editor, 0);
|
||||||
var end = DocumentUtils.offsetToLSPPos(editor, editor.getDocument().getTextLength());
|
var end = DocumentUtils.offsetToLSPPos(editor, editor.getDocument().getTextLength());
|
||||||
return new Range(start, end);
|
return new Range(start, end);
|
||||||
|
@ -1295,14 +1285,16 @@ public class EditorEventManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Tries to go to definition
|
// Tries to go to definition
|
||||||
public void gotoDefinition(PsiElement element) {
|
public boolean gotoDefinition(PsiElement element) {
|
||||||
if (editor.isDisposed()) {
|
if (editor.isDisposed()) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
val sourceOffset = element.getTextOffset();
|
val sourceOffset = element.getTextOffset();
|
||||||
val loc = requestDefinition(DocumentUtils.offsetToLSPPos(editor, sourceOffset));
|
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
|
// Tries to go to declaration / show usages based on the element which is
|
||||||
|
@ -1322,7 +1314,7 @@ public class EditorEventManager {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String locUri = FileUtils.sanitizeURI(loc.getUri());
|
String locUri = FileUtil.sanitizeURI(loc.getUri());
|
||||||
|
|
||||||
if (identifier.getUri().equals(locUri)
|
if (identifier.getUri().equals(locUri)
|
||||||
&& sourceOffset >= DocumentUtils.LSPPosToOffset(editor, loc.getRange().getStart())
|
&& 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;
|
VirtualFile file = null;
|
||||||
try {
|
try {
|
||||||
file = VfsUtil.findFileByURL(new URL(loc.getUri()));
|
file = VfsUtil.findFileByURL(new URL(loc.getUri()));
|
||||||
|
@ -1359,9 +1351,11 @@ public class EditorEventManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
LOG.warn("Empty file for " + loc.getUri());
|
LOG.warn("Empty file for " + loc.getUri());
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void requestAndShowCodeActions() {
|
public void requestAndShowCodeActions() {
|
||||||
|
|
|
@ -15,15 +15,12 @@
|
||||||
*/
|
*/
|
||||||
package com.falsepattern.zigbrains.lsp.editor;
|
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.FileUtils;
|
||||||
import com.falsepattern.zigbrains.lsp.utils.OSUtils;
|
|
||||||
import com.intellij.openapi.editor.Editor;
|
import com.intellij.openapi.editor.Editor;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import org.eclipse.lsp4j.Diagnostic;
|
import org.eclipse.lsp4j.Diagnostic;
|
||||||
|
|
||||||
import java.awt.KeyboardFocusManager;
|
|
||||||
import java.awt.event.KeyEvent;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -86,7 +83,7 @@ public class EditorEventManagerBase {
|
||||||
if (runOnRegistry.containsKey(manager.editor)) {
|
if (runOnRegistry.containsKey(manager.editor)) {
|
||||||
var tasks = runOnRegistry.remove(manager.editor);
|
var tasks = runOnRegistry.remove(manager.editor);
|
||||||
for (var task: tasks) {
|
for (var task: tasks) {
|
||||||
ApplicationUtils.invokeLater(task);
|
ApplicationUtil.invokeLater(task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,7 +94,7 @@ public class EditorEventManagerBase {
|
||||||
var manager = forEditor(editor);
|
var manager = forEditor(editor);
|
||||||
if (manager != null) {
|
if (manager != null) {
|
||||||
for (var task: runnables) {
|
for (var task: runnables) {
|
||||||
ApplicationUtils.invokeLater(task);
|
ApplicationUtil.invokeLater(task);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
runOnRegistry.computeIfAbsent(editor, (ignored) -> new ArrayList<>()).addAll(List.of(runnables));
|
runOnRegistry.computeIfAbsent(editor, (ignored) -> new ArrayList<>()).addAll(List.of(runnables));
|
||||||
|
|
|
@ -15,11 +15,12 @@
|
||||||
*/
|
*/
|
||||||
package com.falsepattern.zigbrains.lsp.listeners;
|
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.IntellijLanguageClient;
|
||||||
import com.falsepattern.zigbrains.lsp.client.languageserver.ServerStatus;
|
import com.falsepattern.zigbrains.lsp.client.languageserver.ServerStatus;
|
||||||
import com.falsepattern.zigbrains.lsp.client.languageserver.wrapper.LanguageServerWrapper;
|
import com.falsepattern.zigbrains.lsp.client.languageserver.wrapper.LanguageServerWrapper;
|
||||||
import com.falsepattern.zigbrains.lsp.editor.EditorEventManagerBase;
|
import com.falsepattern.zigbrains.lsp.editor.EditorEventManagerBase;
|
||||||
import com.falsepattern.zigbrains.lsp.utils.ApplicationUtils;
|
|
||||||
import com.falsepattern.zigbrains.lsp.utils.FileUtils;
|
import com.falsepattern.zigbrains.lsp.utils.FileUtils;
|
||||||
import com.intellij.openapi.diagnostic.Logger;
|
import com.intellij.openapi.diagnostic.Logger;
|
||||||
import com.intellij.openapi.editor.Document;
|
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.fileEditor.FileEditorManager;
|
||||||
import com.intellij.openapi.project.ProjectManager;
|
import com.intellij.openapi.project.ProjectManager;
|
||||||
import com.intellij.openapi.vfs.VirtualFile;
|
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.DidChangeWatchedFilesParams;
|
||||||
import org.eclipse.lsp4j.FileChangeType;
|
import org.eclipse.lsp4j.FileChangeType;
|
||||||
import org.eclipse.lsp4j.FileEvent;
|
import org.eclipse.lsp4j.FileEvent;
|
||||||
|
@ -51,7 +52,7 @@ class LSPFileEventManager {
|
||||||
* @param doc The document
|
* @param doc The document
|
||||||
*/
|
*/
|
||||||
static void willSave(Document doc) {
|
static void willSave(Document doc) {
|
||||||
String uri = FileUtils.VFSToURI(FileDocumentManager.getInstance().getFile(doc));
|
String uri = FileUtil.URIFromVirtualFile(FileDocumentManager.getInstance().getFile(doc));
|
||||||
EditorEventManagerBase.willSave(uri);
|
EditorEventManagerBase.willSave(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,16 +73,16 @@ class LSPFileEventManager {
|
||||||
if (!FileUtils.isFileSupported(file)) {
|
if (!FileUtils.isFileSupported(file)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String uri = FileUtils.VFSToURI(file);
|
String uri = FileUtil.URIFromVirtualFile(file);
|
||||||
if (uri == null) {
|
if (uri == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplicationUtils.invokeAfterPsiEvents(() -> {
|
ApplicationUtil.invokeAfterPsiEvents(() -> {
|
||||||
EditorEventManagerBase.documentSaved(uri);
|
EditorEventManagerBase.documentSaved(uri);
|
||||||
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(uri,
|
FileUtils.findProjectsFor(file)
|
||||||
FileUtils.projectToUri(p), FileChangeType.Changed));
|
.forEach(p -> changedConfiguration(uri, FileUtils.projectToUri(p), FileChangeType.Changed));
|
||||||
});
|
}, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -89,19 +90,19 @@ class LSPFileEventManager {
|
||||||
*
|
*
|
||||||
* @param event The file move event
|
* @param event The file move event
|
||||||
*/
|
*/
|
||||||
static void fileMoved(VirtualFileMoveEvent event) {
|
static void fileMoved(VFileMoveEvent event) {
|
||||||
try {
|
try {
|
||||||
VirtualFile file = event.getFile();
|
VirtualFile file = event.getFile();
|
||||||
if (!FileUtils.isFileSupported(file)) {
|
if (!FileUtils.isFileSupported(file)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String newFileUri = FileUtils.VFSToURI(file);
|
String newFileUri = FileUtil.URIFromVirtualFile(file);
|
||||||
String oldParentUri = FileUtils.VFSToURI(event.getOldParent());
|
String oldParentUri = FileUtil.URIFromVirtualFile(event.getOldParent());
|
||||||
if (newFileUri == null || oldParentUri == null) {
|
if (newFileUri == null || oldParentUri == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String oldFileUri = String.format("%s/%s", oldParentUri, event.getFileName());
|
String oldFileUri = String.format("%s/%s", oldParentUri, event.getFile().getName());
|
||||||
closeAndReopenAffectedFile(file, oldFileUri);
|
closeAndReopenAffectedFile(file, oldFileUri);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.warn("LSP file move event failed due to :", e);
|
LOG.warn("LSP file move event failed due to :", e);
|
||||||
|
@ -117,14 +118,14 @@ class LSPFileEventManager {
|
||||||
if (!FileUtils.isFileSupported(file)) {
|
if (!FileUtils.isFileSupported(file)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String uri = FileUtils.VFSToURI(file);
|
String uri = FileUtil.URIFromVirtualFile(file);
|
||||||
if (uri == null) {
|
if (uri == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ApplicationUtils.invokeAfterPsiEvents(() -> {
|
ApplicationUtil.invokeAfterPsiEvents(() -> {
|
||||||
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(uri,
|
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(uri,
|
||||||
FileUtils.projectToUri(p), FileChangeType.Deleted));
|
FileUtils.projectToUri(p), FileChangeType.Deleted));
|
||||||
});
|
}, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -134,7 +135,7 @@ class LSPFileEventManager {
|
||||||
* @param newFileName the new file name
|
* @param newFileName the new file name
|
||||||
*/
|
*/
|
||||||
static void fileRenamed(String oldFileName, String newFileName) {
|
static void fileRenamed(String oldFileName, String newFileName) {
|
||||||
ApplicationUtils.invokeAfterPsiEvents(() -> {
|
ApplicationUtil.invokeAfterPsiEvents(() -> {
|
||||||
try {
|
try {
|
||||||
// Getting the right file is not trivial here since we only have the file name. Since we have to iterate over
|
// 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.
|
// all opened projects and filter based on the file name.
|
||||||
|
@ -142,22 +143,24 @@ class LSPFileEventManager {
|
||||||
.flatMap(p -> searchFiles(newFileName, p).stream())
|
.flatMap(p -> searchFiles(newFileName, p).stream())
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
ApplicationUtil.invokeLater(() -> {
|
||||||
for (VirtualFile file : files) {
|
for (VirtualFile file : files) {
|
||||||
if (!FileUtils.isFileSupported(file)) {
|
if (!FileUtils.isFileSupported(file)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String newFileUri = FileUtils.VFSToURI(file);
|
String newFileUri = FileUtil.URIFromVirtualFile(file);
|
||||||
String oldFileUri = newFileUri.replace(file.getName(), oldFileName);
|
String oldFileUri = newFileUri.replace(file.getName(), oldFileName);
|
||||||
closeAndReopenAffectedFile(file, oldFileUri);
|
closeAndReopenAffectedFile(file, oldFileUri);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.warn("LSP file rename event failed due to : ", e);
|
LOG.warn("LSP file rename event failed due to : ", e);
|
||||||
}
|
}
|
||||||
});
|
}, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void closeAndReopenAffectedFile(VirtualFile file, String oldFileUri) {
|
private static void closeAndReopenAffectedFile(VirtualFile file, String oldFileUri) {
|
||||||
String newFileUri = FileUtils.VFSToURI(file);
|
String newFileUri = FileUtil.URIFromVirtualFile(file);
|
||||||
|
|
||||||
// Notifies the language server.
|
// Notifies the language server.
|
||||||
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(oldFileUri,
|
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(oldFileUri,
|
||||||
|
@ -172,7 +175,7 @@ class LSPFileEventManager {
|
||||||
if (!newFileUri.equals(oldFileUri)) {
|
if (!newFileUri.equals(oldFileUri)) {
|
||||||
// Re-open file to so that the new editor will be connected to the language server.
|
// Re-open file to so that the new editor will be connected to the language server.
|
||||||
FileEditorManager fileEditorManager = FileEditorManager.getInstance(p);
|
FileEditorManager fileEditorManager = FileEditorManager.getInstance(p);
|
||||||
ApplicationUtils.invokeLater(() -> {
|
ApplicationUtil.invokeLater(() -> {
|
||||||
fileEditorManager.closeFile(file);
|
fileEditorManager.closeFile(file);
|
||||||
fileEditorManager.openFile(file, true);
|
fileEditorManager.openFile(file, true);
|
||||||
});
|
});
|
||||||
|
@ -189,17 +192,17 @@ class LSPFileEventManager {
|
||||||
if (!FileUtils.isFileSupported(file)) {
|
if (!FileUtils.isFileSupported(file)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String uri = FileUtils.VFSToURI(file);
|
String uri = FileUtil.URIFromVirtualFile(file);
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
ApplicationUtils.invokeAfterPsiEvents(() -> {
|
ApplicationUtil.invokeAfterPsiEvents(() -> {
|
||||||
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(uri,
|
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(uri,
|
||||||
FileUtils.projectToUri(p), FileChangeType.Created));
|
FileUtils.projectToUri(p), FileChangeType.Created));
|
||||||
});
|
}, true, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void changedConfiguration(String uri, String projectUri, FileChangeType typ) {
|
private static void changedConfiguration(String uri, String projectUri, FileChangeType typ) {
|
||||||
ApplicationUtils.pool(() -> {
|
ApplicationUtil.pool(() -> {
|
||||||
DidChangeWatchedFilesParams params = getDidChangeWatchedFilesParams(uri, typ);
|
DidChangeWatchedFilesParams params = getDidChangeWatchedFilesParams(uri, typ);
|
||||||
Set<LanguageServerWrapper> wrappers = IntellijLanguageClient.getAllServerWrappersFor(projectUri);
|
Set<LanguageServerWrapper> wrappers = IntellijLanguageClient.getAllServerWrappersFor(projectUri);
|
||||||
if (wrappers == null) {
|
if (wrappers == null) {
|
||||||
|
|
|
@ -16,14 +16,42 @@
|
||||||
package com.falsepattern.zigbrains.lsp.listeners;
|
package com.falsepattern.zigbrains.lsp.listeners;
|
||||||
|
|
||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
import com.intellij.openapi.vfs.VirtualFileCopyEvent;
|
import com.intellij.openapi.vfs.newvfs.BulkFileListener;
|
||||||
import com.intellij.openapi.vfs.VirtualFileEvent;
|
import com.intellij.openapi.vfs.newvfs.events.VFileContentChangeEvent;
|
||||||
import com.intellij.openapi.vfs.VirtualFileListener;
|
import com.intellij.openapi.vfs.newvfs.events.VFileCopyEvent;
|
||||||
import com.intellij.openapi.vfs.VirtualFileMoveEvent;
|
import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent;
|
||||||
import com.intellij.openapi.vfs.VirtualFilePropertyEvent;
|
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;
|
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.
|
* 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.
|
* @param event the event object containing information about the change.
|
||||||
*/
|
*/
|
||||||
@Override
|
public void propertyChanged(@NotNull VFilePropertyChangeEvent event) {
|
||||||
public void propertyChanged(@NotNull VirtualFilePropertyEvent event) {
|
|
||||||
if (event.getPropertyName().equals(VirtualFile.PROP_NAME)) {
|
if (event.getPropertyName().equals(VirtualFile.PROP_NAME)) {
|
||||||
LSPFileEventManager.fileRenamed((String) event.getOldValue(), (String) event.getNewValue());
|
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.
|
* @param event the event object containing information about the change.
|
||||||
*/
|
*/
|
||||||
@Override
|
public void contentsChanged(@NotNull VFileContentChangeEvent event) {
|
||||||
public void contentsChanged(@NotNull VirtualFileEvent event) {
|
|
||||||
LSPFileEventManager.fileChanged(event.getFile());
|
LSPFileEventManager.fileChanged(event.getFile());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,8 +79,7 @@ public class VFSListener implements VirtualFileListener {
|
||||||
*
|
*
|
||||||
* @param event the event object containing information about the change.
|
* @param event the event object containing information about the change.
|
||||||
*/
|
*/
|
||||||
@Override
|
public void fileDeleted(@NotNull VFileDeleteEvent event) {
|
||||||
public void fileDeleted(@NotNull VirtualFileEvent event) {
|
|
||||||
LSPFileEventManager.fileDeleted(event.getFile());
|
LSPFileEventManager.fileDeleted(event.getFile());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,8 +88,7 @@ public class VFSListener implements VirtualFileListener {
|
||||||
*
|
*
|
||||||
* @param event the event object containing information about the change.
|
* @param event the event object containing information about the change.
|
||||||
*/
|
*/
|
||||||
@Override
|
public void fileMoved(@NotNull VFileMoveEvent event) {
|
||||||
public void fileMoved(@NotNull VirtualFileMoveEvent event) {
|
|
||||||
LSPFileEventManager.fileMoved(event);
|
LSPFileEventManager.fileMoved(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,9 +97,8 @@ public class VFSListener implements VirtualFileListener {
|
||||||
*
|
*
|
||||||
* @param event the event object containing information about the change.
|
* @param event the event object containing information about the change.
|
||||||
*/
|
*/
|
||||||
@Override
|
public void fileCopied(@NotNull VFileCopyEvent event) {
|
||||||
public void fileCopied(@NotNull VirtualFileCopyEvent event) {
|
LSPFileEventManager.fileCreated(event.findCreatedFile());
|
||||||
fileCreated(event);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -83,44 +106,7 @@ public class VFSListener implements VirtualFileListener {
|
||||||
*
|
*
|
||||||
* @param event the event object containing information about the change.
|
* @param event the event object containing information about the change.
|
||||||
*/
|
*/
|
||||||
@Override
|
public void fileCreated(@NotNull VFileCreateEvent event) {
|
||||||
public void fileCreated(@NotNull VirtualFileEvent event) {
|
|
||||||
LSPFileEventManager.fileCreated(event.getFile());
|
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;
|
package com.falsepattern.zigbrains.lsp.requests;
|
||||||
|
|
||||||
import com.intellij.openapi.diagnostic.Logger;
|
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.Hover;
|
||||||
import org.eclipse.lsp4j.MarkedString;
|
import org.eclipse.lsp4j.MarkedString;
|
||||||
import org.eclipse.lsp4j.MarkupContent;
|
import org.eclipse.lsp4j.MarkupContent;
|
||||||
|
@ -60,13 +57,9 @@ public class HoverHandler {
|
||||||
"```" + markedString.getLanguage() + " " + markedString.getValue() + "```" :
|
"```" + markedString.getLanguage() + " " + markedString.getValue() + "```" :
|
||||||
"";
|
"";
|
||||||
}
|
}
|
||||||
Parser parser = Parser.builder().build();
|
result.add(string);
|
||||||
HtmlRenderer renderer = HtmlRenderer.builder().build();
|
|
||||||
if (!string.isEmpty()) {
|
|
||||||
result.add(renderer.render(parser.parse(string)));
|
|
||||||
}
|
}
|
||||||
}
|
return String.join("\n", result);
|
||||||
return "<html>" + String.join("\n\n", result) + "</html>";
|
|
||||||
} else {
|
} else {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -75,9 +68,7 @@ public class HoverHandler {
|
||||||
if (markedContent.isEmpty()) {
|
if (markedContent.isEmpty()) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
Parser parser = Parser.builder().build();
|
return markedContent;
|
||||||
HtmlRenderer renderer = HtmlRenderer.builder().build();
|
|
||||||
return "<html>" + renderer.render(parser.parse(markedContent)) + "</html>";
|
|
||||||
} else {
|
} else {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,10 +15,11 @@
|
||||||
*/
|
*/
|
||||||
package com.falsepattern.zigbrains.lsp.requests;
|
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.contributors.psi.LSPPsiElement;
|
||||||
import com.falsepattern.zigbrains.lsp.editor.EditorEventManager;
|
import com.falsepattern.zigbrains.lsp.editor.EditorEventManager;
|
||||||
import com.falsepattern.zigbrains.lsp.editor.EditorEventManagerBase;
|
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.DocumentUtils;
|
||||||
import com.falsepattern.zigbrains.lsp.utils.FileUtils;
|
import com.falsepattern.zigbrains.lsp.utils.FileUtils;
|
||||||
import com.intellij.openapi.command.CommandProcessor;
|
import com.intellij.openapi.command.CommandProcessor;
|
||||||
|
@ -57,8 +58,8 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static com.falsepattern.zigbrains.lsp.utils.ApplicationUtils.invokeLater;
|
import static com.falsepattern.zigbrains.common.util.ApplicationUtil.invokeLater;
|
||||||
import static com.falsepattern.zigbrains.lsp.utils.ApplicationUtils.writeAction;
|
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.DocumentUtils.toEither;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -81,8 +82,8 @@ public class WorkspaceEditHandler {
|
||||||
TextEdit edit = new TextEdit(lspRange, newName);
|
TextEdit edit = new TextEdit(lspRange, newName);
|
||||||
String uri = null;
|
String uri = null;
|
||||||
try {
|
try {
|
||||||
uri = FileUtils.sanitizeURI(
|
uri = FileUtil.sanitizeURI(
|
||||||
new URL(ui.getVirtualFile().getUrl().replace(" ", FileUtils.SPACE_ENCODED)).toURI()
|
new URL(ui.getVirtualFile().getUrl().replace(" ", FileUtil.SPACE_ENCODED)).toURI()
|
||||||
.toString());
|
.toString());
|
||||||
} catch (MalformedURLException | URISyntaxException e) {
|
} catch (MalformedURLException | URISyntaxException e) {
|
||||||
LOG.warn(e);
|
LOG.warn(e);
|
||||||
|
@ -131,7 +132,7 @@ public class WorkspaceEditHandler {
|
||||||
TextDocumentEdit textEdit = tEdit.getLeft();
|
TextDocumentEdit textEdit = tEdit.getLeft();
|
||||||
VersionedTextDocumentIdentifier doc = textEdit.getTextDocument();
|
VersionedTextDocumentIdentifier doc = textEdit.getTextDocument();
|
||||||
int version = doc.getVersion() != null ? doc.getVersion() : Integer.MAX_VALUE;
|
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);
|
EditorEventManager manager = EditorEventManagerBase.forUri(uri);
|
||||||
if (manager != null) {
|
if (manager != null) {
|
||||||
curProject[0] = manager.editor.getProject();
|
curProject[0] = manager.editor.getProject();
|
||||||
|
@ -151,7 +152,7 @@ public class WorkspaceEditHandler {
|
||||||
|
|
||||||
} else if (changes != null) {
|
} else if (changes != null) {
|
||||||
changes.forEach((key, lChanges) -> {
|
changes.forEach((key, lChanges) -> {
|
||||||
String uri = FileUtils.sanitizeURI(key);
|
String uri = FileUtil.sanitizeURI(key);
|
||||||
|
|
||||||
EditorEventManager manager = EditorEventManagerBase.forUri(uri);
|
EditorEventManager manager = EditorEventManagerBase.forUri(uri);
|
||||||
if (manager != null) {
|
if (manager != null) {
|
||||||
|
@ -198,18 +199,18 @@ public class WorkspaceEditHandler {
|
||||||
Project[] projects = ProjectManager.getInstance().getOpenProjects();
|
Project[] projects = ProjectManager.getInstance().getOpenProjects();
|
||||||
//Infer the project from the uri
|
//Infer the project from the uri
|
||||||
Project project = Stream.of(projects)
|
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())
|
.filter(p -> uri.startsWith(p.getLeft())).sorted(Collections.reverseOrder())
|
||||||
.map(ImmutablePair::getRight).findFirst().orElse(projects[0]);
|
.map(ImmutablePair::getRight).findFirst().orElse(projects[0]);
|
||||||
VirtualFile file = null;
|
VirtualFile file = null;
|
||||||
try {
|
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) {
|
} catch (URISyntaxException e) {
|
||||||
LOG.warn(e);
|
LOG.warn(e);
|
||||||
}
|
}
|
||||||
FileEditorManager fileEditorManager = FileEditorManager.getInstance(project);
|
FileEditorManager fileEditorManager = FileEditorManager.getInstance(project);
|
||||||
OpenFileDescriptor descriptor = new OpenFileDescriptor(project, file);
|
OpenFileDescriptor descriptor = new OpenFileDescriptor(project, file);
|
||||||
Editor editor = ApplicationUtils
|
Editor editor = ApplicationUtil
|
||||||
.computableWriteAction(() -> fileEditorManager.openTextEditor(descriptor, false));
|
.computableWriteAction(() -> fileEditorManager.openTextEditor(descriptor, false));
|
||||||
openedEditors.add(file);
|
openedEditors.add(file);
|
||||||
curProject[0] = editor.getProject();
|
curProject[0] = editor.getProject();
|
||||||
|
|
|
@ -31,7 +31,7 @@ import org.jetbrains.annotations.Nullable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
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.max;
|
||||||
import static java.lang.Math.min;
|
import static java.lang.Math.min;
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package com.falsepattern.zigbrains.lsp.utils;
|
package com.falsepattern.zigbrains.lsp.utils;
|
||||||
|
|
||||||
|
import com.falsepattern.zigbrains.common.util.FileUtil;
|
||||||
import com.falsepattern.zigbrains.lsp.IntellijLanguageClient;
|
import com.falsepattern.zigbrains.lsp.IntellijLanguageClient;
|
||||||
import com.falsepattern.zigbrains.lsp.extensions.LSPExtensionManager;
|
import com.falsepattern.zigbrains.lsp.extensions.LSPExtensionManager;
|
||||||
import com.intellij.openapi.diagnostic.Logger;
|
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.fileTypes.FileType;
|
||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
import com.intellij.openapi.project.ProjectManager;
|
import com.intellij.openapi.project.ProjectManager;
|
||||||
|
import com.intellij.openapi.project.ProjectUtil;
|
||||||
import com.intellij.openapi.util.io.FileUtilRt;
|
import com.intellij.openapi.util.io.FileUtilRt;
|
||||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
|
||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
import com.intellij.psi.PsiDocumentManager;
|
import com.intellij.psi.PsiDocumentManager;
|
||||||
import com.intellij.psi.PsiFile;
|
import com.intellij.psi.PsiFile;
|
||||||
import com.intellij.psi.search.FilenameIndex;
|
import com.intellij.psi.search.FilenameIndex;
|
||||||
import com.intellij.psi.search.GlobalSearchScope;
|
import com.intellij.psi.search.GlobalSearchScope;
|
||||||
import com.intellij.testFramework.LightVirtualFileBase;
|
import com.intellij.testFramework.LightVirtualFileBase;
|
||||||
|
import lombok.val;
|
||||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.URISyntaxException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -50,19 +49,12 @@ import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
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
|
* Various file / uri related methods
|
||||||
*/
|
*/
|
||||||
public class FileUtils {
|
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);
|
private static final Logger LOG = Logger.getInstance(FileUtils.class);
|
||||||
|
|
||||||
public static List<Editor> getAllOpenedEditors(Project project) {
|
public static List<Editor> getAllOpenedEditors(Project project) {
|
||||||
|
@ -83,7 +75,7 @@ public class FileUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Editor> getAllOpenedEditorsForUri(@NotNull Project project, String uri) {
|
public static List<Editor> getAllOpenedEditorsForUri(@NotNull Project project, String uri) {
|
||||||
VirtualFile file = virtualFileFromURI(uri);
|
VirtualFile file = FileUtil.virtualFileFromURI(uri);
|
||||||
if (file == null)
|
if (file == null)
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
return getAllOpenedEditorsForVirtualFile(project, file);
|
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) {
|
public static Editor editorFromPsiFile(PsiFile psiFile) {
|
||||||
return editorFromVirtualFile(psiFile.getVirtualFile(), psiFile.getProject());
|
return editorFromVirtualFile(psiFile.getVirtualFile(), psiFile.getProject());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Editor editorFromUri(String uri, Project project) {
|
public static Editor editorFromUri(String uri, Project project) {
|
||||||
return editorFromVirtualFile(virtualFileFromURI(uri), project);
|
return editorFromVirtualFile(FileUtil.virtualFileFromURI(uri), project);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -149,15 +116,6 @@ public class FileUtils {
|
||||||
return null;
|
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
|
* Returns a file type given an editor
|
||||||
*
|
*
|
||||||
|
@ -185,79 +143,14 @@ public class FileUtils {
|
||||||
* @return The URI
|
* @return The URI
|
||||||
*/
|
*/
|
||||||
public static String editorToURIString(Editor editor) {
|
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) {
|
public static VirtualFile virtualFileFromEditor(Editor editor) {
|
||||||
return FileDocumentManager.getInstance().getFile(editor.getDocument());
|
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
|
* Returns the project base dir uri given an editor
|
||||||
*
|
*
|
||||||
|
@ -265,61 +158,37 @@ public class FileUtils {
|
||||||
* @return The project whose the editor belongs
|
* @return The project whose the editor belongs
|
||||||
*/
|
*/
|
||||||
public static String editorToProjectFolderUri(Editor editor) {
|
public static String editorToProjectFolderUri(Editor editor) {
|
||||||
return pathToUri(editorToProjectFolderPath(editor));
|
return FileUtil.pathToUri(editorToProjectFolderPath(editor));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String editorToProjectFolderPath(Editor editor) {
|
public static String editorToProjectFolderPath(Editor editor) {
|
||||||
if (editor != null && editor.getProject() != null && editor.getProject().getBasePath() != null) {
|
if (editor == null)
|
||||||
return new File(editor.getProject().getBasePath()).getAbsolutePath();
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
val project = editor.getProject();
|
||||||
* Transforms a path into an URI string
|
if (project == null)
|
||||||
*
|
return null;
|
||||||
* @param path The path
|
|
||||||
* @return The uri
|
val projectDir = ProjectUtil.guessProjectDir(editor.getProject());
|
||||||
*/
|
if (projectDir == null)
|
||||||
public static String pathToUri(@Nullable String path) {
|
return null;
|
||||||
return path != null ? sanitizeURI(new File(path).toURI().toString()) : null;
|
|
||||||
|
return projectDir.toNioPath().toAbsolutePath().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String projectToUri(Project project) {
|
public static String projectToUri(Project project) {
|
||||||
if (project != null && project.getBasePath() != null) {
|
if (project == null)
|
||||||
return pathToUri(new File(project.getBasePath()).getAbsolutePath());
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
val path = ProjectUtil.guessProjectDir(project);
|
||||||
|
if (path == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return FileUtil.pathToUri(path.toNioPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String documentToUri(Document document) {
|
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)
|
|
||||||
*/
|
|
||||||
public enum OS {
|
|
||||||
WINDOWS, UNIX
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the given virtual file instance is supported by this LS client library.
|
|
||||||
*/
|
|
||||||
public static boolean isFileSupported(@Nullable VirtualFile file) {
|
|
||||||
if (file == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (file instanceof LightVirtualFileBase) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (file.getUrl().isEmpty() || file.getUrl().startsWith("jar:")) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return IntellijLanguageClient.isExtensionSupported(file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -341,6 +210,51 @@ 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given virtual file instance is supported by this LS client library.
|
||||||
|
*/
|
||||||
|
public static boolean isFileSupported(@Nullable VirtualFile file) {
|
||||||
|
if (file == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file instanceof LightVirtualFileBase) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file.getUrl().isEmpty() || file.getUrl().startsWith("jar:")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return IntellijLanguageClient.isExtensionSupported(file);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the file in editor is supported by this LS client library.
|
* 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.Objects;
|
||||||
import java.util.Optional;
|
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 {
|
public final class GUIUtils {
|
||||||
private static final LSPDefaultIconProvider DEFAULT_ICON_PROVIDER = new LSPDefaultIconProvider();
|
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.ExecutionException;
|
||||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
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 com.intellij.util.io.BaseOutputReader;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class ZigCapturingProcessHandler extends CapturingProcessHandler {
|
public class ZigCapturingProcessHandler extends CapturingAnsiEscapesAwareProcessHandler {
|
||||||
public static Optional<ZigCapturingProcessHandler> startProcess(GeneralCommandLine commandLine) {
|
public static Optional<ZigCapturingProcessHandler> startProcess(GeneralCommandLine commandLine) {
|
||||||
try {
|
try {
|
||||||
return Optional.of(new ZigCapturingProcessHandler(commandLine));
|
return Optional.of(new ZigCapturingProcessHandler(commandLine));
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
package com.falsepattern.zigbrains.project.execution.base;
|
package com.falsepattern.zigbrains.project.execution.base;
|
||||||
|
|
||||||
import com.falsepattern.zigbrains.project.execution.base.ZigExecConfigBase;
|
|
||||||
import com.falsepattern.zigbrains.zig.parser.ZigFile;
|
import com.falsepattern.zigbrains.zig.parser.ZigFile;
|
||||||
import com.intellij.execution.actions.ConfigurationContext;
|
import com.intellij.execution.actions.ConfigurationContext;
|
||||||
import com.intellij.execution.actions.LazyRunConfigurationProducer;
|
import com.intellij.execution.actions.LazyRunConfigurationProducer;
|
||||||
|
@ -27,6 +26,8 @@ import com.intellij.psi.PsiElement;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
public abstract class ConfigProducerBase<T extends ZigExecConfigBase<T>> extends LazyRunConfigurationProducer<T> {
|
public abstract class ConfigProducerBase<T extends ZigExecConfigBase<T>> extends LazyRunConfigurationProducer<T> {
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
|
@ -47,7 +48,7 @@ public abstract class ConfigProducerBase<T extends ZigExecConfigBase<T>> extends
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
var theFile = psiFile.getVirtualFile();
|
var theFile = psiFile.getVirtualFile();
|
||||||
var filePath = theFile.getPath();
|
var filePath = theFile.toNioPath();
|
||||||
return setupConfigurationFromContext(configuration, element, filePath, theFile);
|
return setupConfigurationFromContext(configuration, element, filePath, theFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +66,7 @@ public abstract class ConfigProducerBase<T extends ZigExecConfigBase<T>> extends
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
val vFile = file.getVirtualFile();
|
val vFile = file.getVirtualFile();
|
||||||
val filePath = vFile.getPath();
|
val filePath = vFile.toNioPath();
|
||||||
return isConfigurationFromContext(configuration, filePath, vFile, element);
|
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 setupConfigurationFromContext(@NotNull T configuration, PsiElement element, Path filePath, VirtualFile theFile);
|
||||||
protected abstract boolean isConfigurationFromContext(@NotNull T configuration, String filePath, VirtualFile vFile, PsiElement element);
|
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;
|
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.runconfig.ZigProcessHandler;
|
||||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||||
import com.falsepattern.zigbrains.project.util.ProjectUtil;
|
import com.falsepattern.zigbrains.project.util.ProjectUtil;
|
||||||
|
import com.intellij.build.BuildTextConsoleView;
|
||||||
import com.intellij.execution.DefaultExecutionResult;
|
import com.intellij.execution.DefaultExecutionResult;
|
||||||
import com.intellij.execution.ExecutionException;
|
import com.intellij.execution.ExecutionException;
|
||||||
import com.intellij.execution.configurations.CommandLineState;
|
import com.intellij.execution.configurations.CommandLineState;
|
||||||
|
@ -31,6 +31,7 @@ import lombok.val;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
public abstract class ProfileStateBase<T extends ZigExecConfigBase<T>> extends CommandLineState {
|
public abstract class ProfileStateBase<T extends ZigExecConfigBase<T>> extends CommandLineState {
|
||||||
protected final T configuration;
|
protected final T configuration;
|
||||||
|
@ -42,18 +43,20 @@ public abstract class ProfileStateBase<T extends ZigExecConfigBase<T>> extends C
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected @NotNull ProcessHandler startProcess() throws ExecutionException {
|
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) {
|
public GeneralCommandLine getCommandLine(AbstractZigToolchain toolchain, boolean debug) throws ExecutionException {
|
||||||
val workingDirectory = configuration.workingDirectory;
|
val workingDirectory = configuration.getWorkingDirectory();
|
||||||
val zigExecutablePath = toolchain.pathToExecutable("zig");
|
val zigExecutablePath = toolchain.pathToExecutable("zig");
|
||||||
|
|
||||||
return new GeneralCommandLine().withExePath(zigExecutablePath.toString())
|
val cli = new GeneralCommandLine();
|
||||||
.withWorkDirectory(workingDirectory.toString())
|
cli.setExePath(zigExecutablePath.toString());
|
||||||
.withCharset(StandardCharsets.UTF_8)
|
workingDirectory.getPath().ifPresent(x -> cli.setWorkDirectory(x.toFile()));
|
||||||
.withRedirectErrorStream(true)
|
cli.setCharset(StandardCharsets.UTF_8);
|
||||||
.withParameters(configuration.buildCommandLineArgs());
|
cli.setRedirectErrorStream(true);
|
||||||
|
cli.addParameters(debug ? configuration.buildDebugCommandLineArgs() : configuration.buildCommandLineArgs());
|
||||||
|
return cli;
|
||||||
}
|
}
|
||||||
|
|
||||||
public T configuration() {
|
public T configuration() {
|
||||||
|
@ -63,7 +66,7 @@ public abstract class ProfileStateBase<T extends ZigExecConfigBase<T>> extends C
|
||||||
public DefaultExecutionResult executeCommandLine(GeneralCommandLine commandLine, ExecutionEnvironment environment)
|
public DefaultExecutionResult executeCommandLine(GeneralCommandLine commandLine, ExecutionEnvironment environment)
|
||||||
throws ExecutionException {
|
throws ExecutionException {
|
||||||
val handler = startProcess(commandLine);
|
val handler = startProcess(commandLine);
|
||||||
val console = getConsoleBuilder().getConsole();
|
val console = new BuildTextConsoleView(environment.getProject(), false, Collections.emptyList());
|
||||||
console.attachToProcess(handler);
|
console.attachToProcess(handler);
|
||||||
return new DefaultExecutionResult(console, 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.WorkingDirectoryComponent;
|
||||||
import com.falsepattern.zigbrains.project.ui.ZigFilePathPanel;
|
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.ConfigurationException;
|
||||||
import com.intellij.openapi.options.SettingsEditor;
|
import com.intellij.openapi.options.SettingsEditor;
|
||||||
import com.intellij.openapi.ui.LabeledComponent;
|
import com.intellij.openapi.ui.ComboBox;
|
||||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
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.AlignX;
|
||||||
import com.intellij.ui.dsl.builder.AlignY;
|
import com.intellij.ui.dsl.builder.AlignY;
|
||||||
import com.intellij.ui.dsl.builder.Panel;
|
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.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import javax.swing.JComponent;
|
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.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;
|
import static com.intellij.ui.dsl.builder.BuilderKt.panel;
|
||||||
|
|
||||||
public class ZigConfigEditor<T extends ZigExecConfigBase<T>> extends SettingsEditor<T> {
|
public class ZigConfigEditor<T extends ZigExecConfigBase<T>> extends SettingsEditor<T> {
|
||||||
protected final LabeledComponent<TextFieldWithBrowseButton> workingDirectoryComponent =
|
private final ZigExecConfigBase<T> state;
|
||||||
new WorkingDirectoryComponent();
|
private final List<ZigConfigurable.ZigConfigModule<?>> configModules = new ArrayList<>();
|
||||||
|
|
||||||
|
public ZigConfigEditor(ZigExecConfigBase<T> state) {
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void applyEditorTo(@NotNull T s) throws ConfigurationException {
|
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
|
@Override
|
||||||
protected void resetEditorFrom(@NotNull T s) {
|
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
|
@Override
|
||||||
protected final @NotNull JComponent createEditor() {
|
protected final @NotNull JComponent createEditor() {
|
||||||
|
configModules.clear();
|
||||||
|
configModules.addAll(state.getConfigurables().stream().map(ZigConfigurable::createEditor).toList());
|
||||||
return panel((p) -> {
|
return panel((p) -> {
|
||||||
constructPanel(p);
|
for (val module: configModules) {
|
||||||
|
module.construct(p);
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void constructPanel(Panel p) {
|
@Override
|
||||||
|
protected void disposeEditor() {
|
||||||
|
for (val module: configModules) {
|
||||||
|
module.dispose();
|
||||||
|
}
|
||||||
|
configModules.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
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
|
||||||
|
public void readExternal(@NotNull Element element) {
|
||||||
|
try {
|
||||||
|
ElementUtil.readString(element, getSerializedName()).map(Paths::get).ifPresent(x -> path = x);
|
||||||
|
} catch (InvalidPathException ignored){}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
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) -> {
|
p.row(workingDirectoryComponent.getLabel(), (r) -> {
|
||||||
r.cell(workingDirectoryComponent).resizableColumn().align(AlignX.FILL).align(AlignY.FILL);
|
r.cell(workingDirectoryComponent).resizableColumn().align(AlignX.FILL).align(AlignY.FILL);
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static abstract class WithFilePath<T extends ZigExecConfigBase<T>> extends ZigConfigEditor<T> {
|
@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();
|
private final ZigFilePathPanel filePathPanel = new ZigFilePathPanel();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void applyEditorTo(@NotNull T s) throws ConfigurationException {
|
public @Nullable FilePathConfigurable tryMatch(ZigConfigurable<?> cfg) {
|
||||||
super.applyEditorTo(s);
|
return cfg instanceof FilePathConfigurable cfg$ && cfg$.serializedName.equals(serializedName) ? cfg$ : null;
|
||||||
setFilePath(s, filePathPanel.getText());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void resetEditorFrom(@NotNull T s) {
|
protected String getString() {
|
||||||
super.resetEditorFrom(s);
|
return filePathPanel.getText();
|
||||||
filePathPanel.setText(Objects.requireNonNullElse(getFilePath(s), ""));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void constructPanel(Panel p) {
|
protected void setString(String str) {
|
||||||
super.constructPanel(p);
|
filePathPanel.setText(str);
|
||||||
p.row("Target file", (r) -> {
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void construct(Panel p) {
|
||||||
|
p.row(label, (r) -> {
|
||||||
r.cell(filePathPanel).resizableColumn().align(AlignX.FILL).align(AlignY.FILL);
|
r.cell(filePathPanel).resizableColumn().align(AlignX.FILL).align(AlignY.FILL);
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract String getFilePath(T config);
|
@Override
|
||||||
protected abstract void setFilePath(T config, String path);
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@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.configurations.LocatableConfigurationBase;
|
||||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||||
import com.intellij.openapi.project.Project;
|
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.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.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Getter
|
||||||
public abstract class ZigExecConfigBase<T extends ZigExecConfigBase<T>> extends LocatableConfigurationBase<ProfileStateBase<T>> {
|
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) {
|
public ZigExecConfigBase(@NotNull Project project, @NotNull ConfigurationFactory factory, @Nullable String name) {
|
||||||
super(project, factory, name);
|
super(project, factory, name);
|
||||||
workingDirectory = project.isDefault() ? null : Optional.ofNullable(project.getBasePath())
|
workingDirectory.setPath(getProject().isDefault() ? null : Optional.ofNullable(ProjectUtil.guessProjectDir(getProject()))
|
||||||
.map(Path::of)
|
.map(VirtualFile::toNioPath)
|
||||||
.orElse(null);
|
.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 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
|
@Override
|
||||||
public abstract @Nullable @NlsActions.ActionText String suggestedName();
|
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)
|
public abstract @Nullable ProfileStateBase<T> getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment)
|
||||||
throws ExecutionException;
|
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.execution.configurations.ConfigurationFactory;
|
||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
import com.intellij.psi.PsiElement;
|
import com.intellij.psi.PsiElement;
|
||||||
|
import lombok.val;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
public class ConfigProducerBuild extends ConfigProducerBase<ZigExecConfigBuild> {
|
public class ConfigProducerBuild extends ConfigProducerBase<ZigExecConfigBuild> {
|
||||||
@Override
|
@Override
|
||||||
public @NotNull ConfigurationFactory getConfigurationFactory() {
|
public @NotNull ConfigurationFactory getConfigurationFactory() {
|
||||||
|
@ -29,7 +32,7 @@ public class ConfigProducerBuild extends ConfigProducerBase<ZigExecConfigBuild>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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)) {
|
if (ZigLineMarkerBuild.UTILITY_INSTANCE.elementMatches(element)) {
|
||||||
configuration.setName("Build");
|
configuration.setName("Build");
|
||||||
return true;
|
return true;
|
||||||
|
@ -38,7 +41,11 @@ public class ConfigProducerBuild extends ConfigProducerBase<ZigExecConfigBuild>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isConfigurationFromContext(@NotNull ZigExecConfigBuild configuration, String filePath, VirtualFile vFile, PsiElement element) {
|
protected boolean isConfigurationFromContext(@NotNull ZigExecConfigBuild configuration, Path filePath, VirtualFile vFile, PsiElement element) {
|
||||||
return true;
|
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;
|
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.ZigConfigEditor;
|
||||||
import com.falsepattern.zigbrains.project.execution.base.ZigExecConfigBase;
|
import com.falsepattern.zigbrains.project.execution.base.ZigExecConfigBase;
|
||||||
import com.falsepattern.zigbrains.project.util.ElementUtil;
|
|
||||||
import com.intellij.execution.Executor;
|
import com.intellij.execution.Executor;
|
||||||
import com.intellij.execution.configurations.ConfigurationFactory;
|
import com.intellij.execution.configurations.ConfigurationFactory;
|
||||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||||
import com.intellij.openapi.options.ConfigurationException;
|
|
||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
import com.intellij.openapi.util.InvalidDataException;
|
import lombok.Getter;
|
||||||
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.val;
|
import lombok.val;
|
||||||
import org.apache.groovy.util.Arrays;
|
|
||||||
import org.jdom.Element;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.List;
|
||||||
|
|
||||||
|
@Getter
|
||||||
public class ZigExecConfigBuild extends ZigExecConfigBase<ZigExecConfigBuild> {
|
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) {
|
public ZigExecConfigBuild(@NotNull Project project, @NotNull ConfigurationFactory factory) {
|
||||||
super(project, factory, "Zig Build");
|
super(project, factory, "Zig Build");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] buildCommandLineArgs() {
|
public String[] buildCommandLineArgs() {
|
||||||
val base = new String[]{"build"};
|
val base = new String[]{"build", "--color", colored.colored ? "on" : "off"};
|
||||||
if (extraArguments.isBlank()) {
|
return CollectionUtil.concat(base, extraArgs.args);
|
||||||
return base;
|
|
||||||
} else {
|
|
||||||
return Arrays.concat(base, extraArguments.split(" "));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -59,54 +51,21 @@ public class ZigExecConfigBuild extends ZigExecConfigBase<ZigExecConfigBuild> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull Editor getConfigurationEditor() {
|
public @NotNull List<ZigConfigEditor.ZigConfigurable<?>> getConfigurables() {
|
||||||
return new Editor();
|
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
|
@Override
|
||||||
public @Nullable ProfileStateBuild getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) {
|
public @Nullable ProfileStateBuild getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) {
|
||||||
return new ProfileStateBuild(environment, this);
|
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 com.intellij.psi.PsiElement;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
public class ConfigProducerRun extends ConfigProducerBase<ZigExecConfigRun> {
|
public class ConfigProducerRun extends ConfigProducerBase<ZigExecConfigRun> {
|
||||||
@Override
|
@Override
|
||||||
public @NotNull ConfigurationFactory getConfigurationFactory() {
|
public @NotNull ConfigurationFactory getConfigurationFactory() {
|
||||||
|
@ -30,9 +33,9 @@ public class ConfigProducerRun extends ConfigProducerBase<ZigExecConfigRun> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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)) {
|
if (ZigLineMarkerRun.UTILITY_INSTANCE.elementMatches(element)) {
|
||||||
configuration.filePath = filePath;
|
configuration.getFilePath().setPath(filePath);
|
||||||
configuration.setName(theFile.getPresentableName());
|
configuration.setName(theFile.getPresentableName());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -40,8 +43,8 @@ public class ConfigProducerRun extends ConfigProducerBase<ZigExecConfigRun> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isConfigurationFromContext(@NotNull ZigExecConfigRun configuration, String filePath, VirtualFile vFile, PsiElement element) {
|
protected boolean isConfigurationFromContext(@NotNull ZigExecConfigRun configuration, Path filePath, VirtualFile vFile, PsiElement element) {
|
||||||
return configuration.filePath.equals(filePath);
|
return Objects.equals(configuration.getFilePath().getPath().orElse(null), filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -16,31 +16,42 @@
|
||||||
|
|
||||||
package com.falsepattern.zigbrains.project.execution.run;
|
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.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.Executor;
|
||||||
import com.intellij.execution.configurations.ConfigurationFactory;
|
import com.intellij.execution.configurations.ConfigurationFactory;
|
||||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
import com.intellij.openapi.util.InvalidDataException;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.val;
|
||||||
import org.jdom.Element;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@Setter
|
import java.util.List;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public class ZigExecConfigRun extends ZigExecConfigBase<ZigExecConfigRun> {
|
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) {
|
public ZigExecConfigRun(@NotNull Project project, @NotNull ConfigurationFactory factory) {
|
||||||
super(project, factory, "Zig Run");
|
super(project, factory, "Zig Run");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] buildCommandLineArgs() {
|
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
|
@Override
|
||||||
|
@ -49,42 +60,22 @@ public class ZigExecConfigRun extends ZigExecConfigBase<ZigExecConfigRun> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull Editor getConfigurationEditor() {
|
public ZigExecConfigRun clone() {
|
||||||
return new Editor();
|
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
|
@Override
|
||||||
public @Nullable ProfileStateRun getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) {
|
public @Nullable ProfileStateRun getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) {
|
||||||
return new ProfileStateRun(environment, this);
|
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 com.intellij.psi.PsiElement;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
public class ConfigProducerTest extends ConfigProducerBase<ZigExecConfigTest> {
|
public class ConfigProducerTest extends ConfigProducerBase<ZigExecConfigTest> {
|
||||||
@Override
|
@Override
|
||||||
public @NotNull ConfigurationFactory getConfigurationFactory() {
|
public @NotNull ConfigurationFactory getConfigurationFactory() {
|
||||||
|
@ -31,9 +34,9 @@ public class ConfigProducerTest extends ConfigProducerBase<ZigExecConfigTest> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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)) {
|
if (ZigLineMarkerTest.UTILITY_INSTANCE.elementMatches(element)) {
|
||||||
configuration.filePath = filePath;
|
configuration.getFilePath().setPath(filePath);
|
||||||
configuration.setName("all tests in " + theFile.getPresentableName());
|
configuration.setName("all tests in " + theFile.getPresentableName());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -41,8 +44,8 @@ public class ConfigProducerTest extends ConfigProducerBase<ZigExecConfigTest> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isConfigurationFromContext(@NotNull ZigExecConfigTest configuration, String filePath, VirtualFile vFile, PsiElement element) {
|
protected boolean isConfigurationFromContext(@NotNull ZigExecConfigTest configuration, Path filePath, VirtualFile vFile, PsiElement element) {
|
||||||
return configuration.filePath.equals(filePath);
|
return Objects.equals(configuration.getFilePath().getPath().orElse(null), filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -16,27 +16,42 @@
|
||||||
|
|
||||||
package com.falsepattern.zigbrains.project.execution.test;
|
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.ProfileStateBase;
|
||||||
import com.falsepattern.zigbrains.project.execution.base.ZigExecConfigBase;
|
|
||||||
import com.falsepattern.zigbrains.project.execution.base.ZigConfigEditor;
|
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.Executor;
|
||||||
import com.intellij.execution.configurations.ConfigurationFactory;
|
import com.intellij.execution.configurations.ConfigurationFactory;
|
||||||
import com.intellij.execution.configurations.RunConfiguration;
|
|
||||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||||
import com.intellij.openapi.options.SettingsEditor;
|
|
||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.val;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Getter
|
||||||
public class ZigExecConfigTest extends ZigExecConfigBase<ZigExecConfigTest> {
|
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) {
|
public ZigExecConfigTest(@NotNull Project project, @NotNull ConfigurationFactory factory) {
|
||||||
super(project, factory, "Zig Test");
|
super(project, factory, "Zig Test");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] buildCommandLineArgs() {
|
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
|
@Override
|
||||||
|
@ -50,20 +65,16 @@ public class ZigExecConfigTest extends ZigExecConfigBase<ZigExecConfigTest> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull SettingsEditor<? extends RunConfiguration> getConfigurationEditor() {
|
public ZigExecConfigTest clone() {
|
||||||
return new Editor();
|
val clone = super.clone();
|
||||||
}
|
clone.filePath = filePath.clone();
|
||||||
|
clone.colored = colored.clone();
|
||||||
public static class Editor extends ZigConfigEditor.WithFilePath<ZigExecConfigTest> {
|
clone.optimization = optimization.clone();
|
||||||
|
return clone;
|
||||||
@Override
|
|
||||||
protected String getFilePath(ZigExecConfigTest config) {
|
|
||||||
return config.filePath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setFilePath(ZigExecConfigTest config, String path) {
|
public @NotNull List<ZigConfigEditor.ZigConfigurable<?>> getConfigurables() {
|
||||||
config.filePath = path;
|
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.Notification;
|
||||||
import com.intellij.notification.NotificationType;
|
import com.intellij.notification.NotificationType;
|
||||||
import com.intellij.notification.Notifications;
|
import com.intellij.notification.Notifications;
|
||||||
import com.intellij.openapi.application.ApplicationManager;
|
|
||||||
import com.intellij.openapi.application.WriteAction;
|
import com.intellij.openapi.application.WriteAction;
|
||||||
import com.intellij.openapi.module.Module;
|
import com.intellij.openapi.module.Module;
|
||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
|
|
|
@ -16,16 +16,19 @@
|
||||||
|
|
||||||
package com.falsepattern.zigbrains.project.runconfig;
|
package com.falsepattern.zigbrains.project.runconfig;
|
||||||
|
|
||||||
|
import com.falsepattern.zigbrains.common.util.StringUtil;
|
||||||
import com.intellij.execution.ExecutionException;
|
import com.intellij.execution.ExecutionException;
|
||||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||||
import com.intellij.execution.configurations.PtyCommandLine;
|
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 com.pty4j.PtyProcess;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
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 {
|
public ZigProcessHandler(@NotNull GeneralCommandLine commandLine) throws ExecutionException {
|
||||||
super(commandLine);
|
super(commandLine);
|
||||||
setHasPty(commandLine instanceof PtyCommandLine);
|
setHasPty(commandLine instanceof PtyCommandLine);
|
||||||
|
@ -37,4 +40,9 @@ public class ZigProcessHandler extends KillableProcessHandler {
|
||||||
setHasPty(process instanceof PtyProcess);
|
setHasPty(process instanceof PtyProcess);
|
||||||
setShouldDestroyProcessRecursively(!hasPty());
|
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.ExecutionException;
|
||||||
import com.intellij.execution.configurations.RunProfile;
|
import com.intellij.execution.configurations.RunProfile;
|
||||||
import com.intellij.execution.executors.DefaultRunExecutor;
|
import com.intellij.execution.executors.DefaultRunExecutor;
|
||||||
|
import com.intellij.execution.runners.DefaultProgramRunnerKt;
|
||||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||||
import com.intellij.execution.ui.RunContentDescriptor;
|
import com.intellij.execution.ui.RunContentDescriptor;
|
||||||
import com.intellij.execution.runners.DefaultProgramRunnerKt;
|
|
||||||
import org.jetbrains.annotations.NonNls;
|
import org.jetbrains.annotations.NonNls;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
@ -52,6 +52,6 @@ public class ZigRegularRunner extends ZigProgramRunnerBase<ProfileStateBase<?>>
|
||||||
@Override
|
@Override
|
||||||
protected @Nullable RunContentDescriptor doExecute(ProfileStateBase<?> state, AbstractZigToolchain toolchain, ExecutionEnvironment environment)
|
protected @Nullable RunContentDescriptor doExecute(ProfileStateBase<?> state, AbstractZigToolchain toolchain, ExecutionEnvironment environment)
|
||||||
throws ExecutionException {
|
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.Project;
|
||||||
import com.intellij.openapi.project.ProjectUtil;
|
import com.intellij.openapi.project.ProjectUtil;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
public class ToolchainZLSConfigProvider implements ZLSConfigProvider {
|
public class ToolchainZLSConfigProvider implements ZLSConfigProvider {
|
||||||
@Override
|
@Override
|
||||||
public @NotNull ZLSConfig getEnvironment(Project project) {
|
public void getEnvironment(Project project, ZLSConfig.ZLSConfigBuilder builder) {
|
||||||
val projectSettings = ZigProjectSettingsService.getInstance(project);
|
val projectSettings = ZigProjectSettingsService.getInstance(project);
|
||||||
val toolchain = projectSettings.getToolchain();
|
val toolchain = projectSettings.getToolchain();
|
||||||
if (toolchain == null)
|
if (toolchain == null)
|
||||||
return ZLSConfig.EMPTY;
|
return;
|
||||||
val projectDir = ProjectUtil.guessProjectDir(project);
|
val projectDir = ProjectUtil.guessProjectDir(project);
|
||||||
val env = toolchain.zig().getEnv(projectDir == null ? Path.of(".") : projectDir.toNioPath());
|
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.AbstractZigToolchain;
|
||||||
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainEnvironmentSerializable;
|
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainEnvironmentSerializable;
|
||||||
import com.falsepattern.zigbrains.project.util.CLIUtil;
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.intellij.execution.process.ProcessOutput;
|
import com.intellij.execution.process.ProcessOutput;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
|
@ -17,17 +17,24 @@
|
||||||
package com.falsepattern.zigbrains.project.ui;
|
package com.falsepattern.zigbrains.project.ui;
|
||||||
|
|
||||||
import com.intellij.execution.ExecutionBundle;
|
import com.intellij.execution.ExecutionBundle;
|
||||||
|
import com.intellij.openapi.Disposable;
|
||||||
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
|
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
|
||||||
import com.intellij.openapi.ui.LabeledComponent;
|
import com.intellij.openapi.ui.LabeledComponent;
|
||||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||||
|
|
||||||
public class WorkingDirectoryComponent extends LabeledComponent<TextFieldWithBrowseButton> {
|
public class WorkingDirectoryComponent extends LabeledComponent<TextFieldWithBrowseButton> implements Disposable {
|
||||||
public WorkingDirectoryComponent() {
|
private final TextFieldWithBrowseButton field;
|
||||||
var component = new TextFieldWithBrowseButton();
|
public WorkingDirectoryComponent(Disposable parent) {
|
||||||
|
field = new TextFieldWithBrowseButton(null, parent);
|
||||||
var fileChooser = FileChooserDescriptorFactory.createSingleFolderDescriptor();
|
var fileChooser = FileChooserDescriptorFactory.createSingleFolderDescriptor();
|
||||||
fileChooser.setTitle(ExecutionBundle.message("select.working.directory.message"));
|
fileChooser.setTitle(ExecutionBundle.message("select.working.directory.message"));
|
||||||
component.addBrowseFolderListener(null, null, null, fileChooser);
|
field.addBrowseFolderListener(null, null, null, fileChooser);
|
||||||
setComponent(component);
|
setComponent(field);
|
||||||
setText(ExecutionBundle.message("run.configuration.working.directory.label"));
|
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.TextBrowseFolderListener;
|
||||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||||
import com.intellij.ui.TextAccessor;
|
import com.intellij.ui.TextAccessor;
|
||||||
import com.intellij.ui.components.JBTextField;
|
|
||||||
|
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import java.awt.BorderLayout;
|
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.configurations.GeneralCommandLine;
|
||||||
import com.intellij.execution.process.CapturingProcessHandler;
|
import com.intellij.execution.process.CapturingProcessHandler;
|
||||||
import com.intellij.execution.process.ProcessOutput;
|
import com.intellij.execution.process.ProcessOutput;
|
||||||
|
import com.intellij.openapi.options.ConfigurationException;
|
||||||
import com.intellij.openapi.progress.ProgressIndicator;
|
import com.intellij.openapi.progress.ProgressIndicator;
|
||||||
import com.intellij.openapi.progress.ProgressManager;
|
import com.intellij.openapi.progress.ProgressManager;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
public class CLIUtil {
|
public class CLIUtil {
|
||||||
public static Optional<ProcessOutput> execute(GeneralCommandLine cli, int timeoutMillis) {
|
public static Optional<ProcessOutput> execute(GeneralCommandLine cli, int timeoutMillis) {
|
||||||
|
@ -48,4 +51,72 @@ public class CLIUtil {
|
||||||
return handler.runProcess();
|
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 lombok.val;
|
||||||
import org.jdom.Element;
|
import org.jdom.Element;
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public class ElementUtil {
|
public class ElementUtil {
|
||||||
public static @Nullable String readString(Element element, String name) {
|
public static Optional<String> readString(Element element, String name) {
|
||||||
return element.getChildren()
|
return element.getChildren()
|
||||||
.stream()
|
.stream()
|
||||||
.filter(it -> it.getName()
|
.filter(it -> it.getName()
|
||||||
|
@ -29,15 +31,82 @@ public class ElementUtil {
|
||||||
it.getAttributeValue("name")
|
it.getAttributeValue("name")
|
||||||
.equals(name))
|
.equals(name))
|
||||||
.findAny()
|
.findAny()
|
||||||
.map(it -> it.getAttributeValue("value"))
|
.map(it -> it.getAttributeValue("value"));
|
||||||
.orElse(null);
|
}
|
||||||
|
|
||||||
|
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) {
|
public static void writeString(Element element, String name, String value) {
|
||||||
val option = new Element("ZigBrainsOption");
|
val option = new Element("ZigBrainsOption");
|
||||||
option.setAttribute("name", name);
|
option.setAttribute("name", name);
|
||||||
option.setAttribute("value", value);
|
option.setAttribute("value", Objects.requireNonNullElse(value, ""));
|
||||||
|
|
||||||
element.addContent(option);
|
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