-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #15296 from geoand/open-in-ide
Add the ability to open classes from the DevUI in the IDE
- Loading branch information
Showing
11 changed files
with
663 additions
and
5 deletions.
There are no files selected for viewing
19 changes: 19 additions & 0 deletions
19
core/deployment/src/main/java/io/quarkus/deployment/ide/EffectiveIdeBuildItem.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package io.quarkus.deployment.ide; | ||
|
||
import io.quarkus.builder.item.SimpleBuildItem; | ||
|
||
/** | ||
* Contains the IDE to be opened when a request to open a class is made | ||
*/ | ||
public final class EffectiveIdeBuildItem extends SimpleBuildItem { | ||
|
||
private final Ide ide; | ||
|
||
public EffectiveIdeBuildItem(Ide ide) { | ||
this.ide = ide; | ||
} | ||
|
||
public Ide getIde() { | ||
return ide; | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
core/deployment/src/main/java/io/quarkus/deployment/ide/Ide.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package io.quarkus.deployment.ide; | ||
|
||
public enum Ide { | ||
IDEA, | ||
ECLIPSE, | ||
VSCODE | ||
} |
22 changes: 22 additions & 0 deletions
22
core/deployment/src/main/java/io/quarkus/deployment/ide/IdeConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package io.quarkus.deployment.ide; | ||
|
||
import io.quarkus.runtime.annotations.ConfigItem; | ||
import io.quarkus.runtime.annotations.ConfigRoot; | ||
|
||
@ConfigRoot | ||
public class IdeConfig { | ||
|
||
/** | ||
* The Ide to use to open files from the DevUI. | ||
* {@code auto} means that Quarkus will attempt to determine the Ide being used. | ||
*/ | ||
@ConfigItem(defaultValue = "auto") | ||
public Target target; | ||
|
||
enum Target { | ||
auto, | ||
idea, | ||
vscode, | ||
eclipse | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
core/deployment/src/main/java/io/quarkus/deployment/ide/IdeFileBuildItem.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package io.quarkus.deployment.ide; | ||
|
||
import java.util.Set; | ||
|
||
import io.quarkus.builder.item.SimpleBuildItem; | ||
|
||
/** | ||
* Contains the set of IDEs that could be potentially linked to project based | ||
* on the files present in the project | ||
*/ | ||
final class IdeFileBuildItem extends SimpleBuildItem { | ||
|
||
private final Set<Ide> detectedIDEs; | ||
|
||
IdeFileBuildItem(Set<Ide> detectedIDEs) { | ||
this.detectedIDEs = detectedIDEs; | ||
} | ||
|
||
Set<Ide> getDetectedIDEs() { | ||
return detectedIDEs; | ||
} | ||
} |
163 changes: 163 additions & 0 deletions
163
core/deployment/src/main/java/io/quarkus/deployment/ide/IdeProcessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
package io.quarkus.deployment.ide; | ||
|
||
import java.lang.reflect.InvocationTargetException; | ||
import java.lang.reflect.Method; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.Collections; | ||
import java.util.HashMap; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
import java.util.Set; | ||
import java.util.function.Predicate; | ||
import java.util.stream.Stream; | ||
|
||
import io.quarkus.deployment.annotations.BuildStep; | ||
import io.quarkus.deployment.pkg.builditem.BuildSystemTargetBuildItem; | ||
import io.quarkus.runtime.util.JavaVersionUtil; | ||
|
||
public class IdeProcessor { | ||
|
||
private static Map<String, List<Ide>> IDE_MARKER_FILES = new HashMap<>(); | ||
private static Map<Predicate<ProcessInfo>, Ide> IDE_PROCESSES = new HashMap<>(); | ||
|
||
static { | ||
IDE_MARKER_FILES.put(".idea", Collections.singletonList(Ide.IDEA)); | ||
IDE_MARKER_FILES.put(".project", Arrays.asList(Ide.VSCODE, Ide.ECLIPSE)); | ||
IDE_MARKER_FILES = Collections.unmodifiableMap(IDE_MARKER_FILES); | ||
|
||
IDE_PROCESSES.put((processInfo -> processInfo.getCommand().contains("idea")), Ide.IDEA); | ||
IDE_PROCESSES.put((processInfo -> processInfo.getCommand().contains("code")), Ide.VSCODE); | ||
IDE_PROCESSES.put((processInfo -> processInfo.getCommand().contains("eclipse")), Ide.ECLIPSE); // TODO: I have no idea if this is correct | ||
IDE_PROCESSES = Collections.unmodifiableMap(IDE_PROCESSES); | ||
} | ||
|
||
@BuildStep | ||
public EffectiveIdeBuildItem effectiveIde(IdeConfig ideConfig, IdeFileBuildItem ideFile, | ||
IdeRunningProcessBuildItem ideRunningProcess) { | ||
Ide result = null; | ||
if (ideConfig.target == IdeConfig.Target.auto) { | ||
|
||
// the idea here is to auto-detect the special files that IDEs create | ||
// and also the running IDE process if need be | ||
|
||
if (ideFile.getDetectedIDEs().size() == 1) { | ||
result = ideFile.getDetectedIDEs().iterator().next(); | ||
} else { | ||
Set<Ide> runningIdes = ideRunningProcess.getDetectedIDEs(); | ||
if (runningIdes.size() == 1) { | ||
result = runningIdes.iterator().next(); | ||
} else { | ||
List<Ide> matches = new ArrayList<>(); | ||
for (Ide file : ideFile.getDetectedIDEs()) { | ||
for (Ide process : runningIdes) { | ||
if (file == process) { | ||
matches.add(file); | ||
} | ||
} | ||
} | ||
if (matches.size() == 1) { | ||
result = matches.get(0); | ||
} | ||
} | ||
} | ||
} else { | ||
if (ideConfig.target == IdeConfig.Target.idea) { | ||
result = Ide.IDEA; | ||
} else if (ideConfig.target == IdeConfig.Target.eclipse) { | ||
result = Ide.ECLIPSE; | ||
} else if (ideConfig.target == IdeConfig.Target.vscode) { | ||
result = Ide.VSCODE; | ||
} | ||
} | ||
|
||
if (result == null) { | ||
return null; | ||
} | ||
|
||
return new EffectiveIdeBuildItem(result); | ||
} | ||
|
||
@BuildStep | ||
public IdeFileBuildItem detectIdeFiles(BuildSystemTargetBuildItem buildSystemTarget) { | ||
Set<Ide> result = new HashSet<>(2); | ||
Path projectRoot = buildSystemTarget.getOutputDirectory().getParent(); | ||
IDE_MARKER_FILES.forEach((file, ides) -> { | ||
if (Files.exists(projectRoot.resolve(file))) { | ||
result.addAll(ides); | ||
} | ||
}); | ||
return new IdeFileBuildItem(result); | ||
} | ||
|
||
@BuildStep | ||
public IdeRunningProcessBuildItem detectRunningIdeProcesses() { | ||
Set<Ide> result = new HashSet<>(2); | ||
List<ProcessInfo> processInfos = ProcessUtil.runningProcesses(); | ||
for (ProcessInfo processInfo : processInfos) { | ||
for (Map.Entry<Predicate<ProcessInfo>, Ide> entry : IDE_PROCESSES.entrySet()) { | ||
if (entry.getKey().test(processInfo)) { | ||
result.add(entry.getValue()); | ||
break; | ||
} | ||
} | ||
} | ||
return new IdeRunningProcessBuildItem(result); | ||
} | ||
|
||
// TODO: remove when we move to Java 11 and just call the methods of 'java.lang.ProcessHandle' | ||
private static class ProcessUtil { | ||
|
||
/** | ||
* Returns a list of running processes | ||
* Only works for Java 11+ | ||
*/ | ||
@SuppressWarnings({ "rawtypes", "unchecked" }) | ||
public static List<ProcessInfo> runningProcesses() { | ||
if (!JavaVersionUtil.isJava11OrHigher()) { | ||
return Collections.emptyList(); | ||
} | ||
// we can't use ProcessHandle directly as it is Java 9+, so we need to do reflection to the get the info we need | ||
try { | ||
Class processHandlerClass = Class.forName("java.lang.ProcessHandle"); | ||
Method allProcessesMethod = processHandlerClass.getMethod("allProcesses"); | ||
Method processHandleInfoMethod = processHandlerClass.getMethod("info"); | ||
Class processHandleInfoClass = Class.forName("java.lang.ProcessHandle$Info"); | ||
Method processHandleInfoCommandMethod = processHandleInfoClass.getMethod("command"); | ||
Stream<Object> allProcessesResult = (Stream<Object>) allProcessesMethod.invoke(null); | ||
List<ProcessInfo> result = new ArrayList<>(); | ||
allProcessesResult.forEach(o -> { | ||
try { | ||
Object processHandleInfo = processHandleInfoMethod.invoke(o); | ||
Optional<String> command = (Optional<String>) processHandleInfoCommandMethod.invoke(processHandleInfo); | ||
if (command.isPresent()) { | ||
result.add(new ProcessInfo(command.get())); | ||
} | ||
} catch (IllegalAccessException | InvocationTargetException e) { | ||
throw new RuntimeException(e); | ||
} | ||
}); | ||
return result; | ||
} catch (Exception e) { | ||
throw new RuntimeException("Unable to determine running processes", e); | ||
} | ||
} | ||
} | ||
|
||
private static class ProcessInfo { | ||
// the executable pathname of the process. | ||
private final String command; | ||
|
||
public ProcessInfo(String command) { | ||
this.command = command; | ||
} | ||
|
||
public String getCommand() { | ||
return command; | ||
} | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
core/deployment/src/main/java/io/quarkus/deployment/ide/IdeRunningProcessBuildItem.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package io.quarkus.deployment.ide; | ||
|
||
import java.util.Set; | ||
|
||
import io.quarkus.builder.item.SimpleBuildItem; | ||
|
||
/** | ||
* Contains the set of IDEs that are running as processes | ||
*/ | ||
final class IdeRunningProcessBuildItem extends SimpleBuildItem { | ||
|
||
private final Set<Ide> detectedIDEs; | ||
|
||
IdeRunningProcessBuildItem(Set<Ide> detectedIDEs) { | ||
this.detectedIDEs = detectedIDEs; | ||
} | ||
|
||
Set<Ide> getDetectedIDEs() { | ||
return detectedIDEs; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.