-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Create a Quarkus Dev Explorer in the IDE
Fixes #1175 Signed-off-by: azerr <[email protected]>
- Loading branch information
1 parent
7d107b0
commit 9a6cd96
Showing
23 changed files
with
887 additions
and
34 deletions.
There are no files selected for viewing
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
68 changes: 68 additions & 0 deletions
68
src/main/java/com/redhat/devtools/intellij/quarkus/explorer/QuarkusActionNode.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,68 @@ | ||
package com.redhat.devtools.intellij.quarkus.explorer; | ||
|
||
import com.intellij.icons.AllIcons; | ||
import com.intellij.openapi.application.ApplicationManager; | ||
import com.intellij.openapi.module.Module; | ||
import com.intellij.ui.AnimatedIcon; | ||
import com.intellij.ui.treeStructure.Tree; | ||
|
||
import javax.swing.*; | ||
import javax.swing.tree.DefaultMutableTreeNode; | ||
import javax.swing.tree.DefaultTreeModel; | ||
import javax.swing.tree.TreePath; | ||
|
||
public abstract class QuarkusActionNode extends DefaultMutableTreeNode { | ||
|
||
private static final Icon RUNNING_ICON = new AnimatedIcon.Default(); | ||
private final String name; | ||
|
||
private final QuarkusProjectNode projectNode; | ||
|
||
public QuarkusActionNode(String name, QuarkusProjectNode projectNode) { | ||
this.name = name; | ||
this.projectNode = projectNode; | ||
} | ||
|
||
public String getName() { | ||
return name; | ||
} | ||
|
||
public Module getModule() { | ||
return projectNode.getModule(); | ||
} | ||
|
||
public QuarkusProjectNode getProjectNode() { | ||
return projectNode; | ||
} | ||
|
||
public String getDisplayName() { | ||
return name; | ||
} | ||
|
||
public Icon getIcon() { | ||
return AllIcons.Actions.InlayGear; | ||
} | ||
|
||
public void refreshNode(boolean nodeStructureChanged) { | ||
invokeLater(() -> { | ||
var tree = projectNode.getTree(); | ||
if (nodeStructureChanged) { | ||
((DefaultTreeModel) tree.getModel()).nodeStructureChanged(this); | ||
} else { | ||
((DefaultTreeModel) tree.getModel()).nodeChanged(this); | ||
} | ||
var treePath = new TreePath(this.getPath()); | ||
tree.expandPath(treePath); | ||
}); | ||
} | ||
|
||
private static void invokeLater(Runnable runnable) { | ||
if (ApplicationManager.getApplication().isDispatchThread()) { | ||
runnable.run(); | ||
} else { | ||
ApplicationManager.getApplication().invokeLater(runnable); | ||
} | ||
} | ||
|
||
public abstract String getActionId(); | ||
} |
209 changes: 209 additions & 0 deletions
209
src/main/java/com/redhat/devtools/intellij/quarkus/explorer/QuarkusExplorer.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,209 @@ | ||
package com.redhat.devtools.intellij.quarkus.explorer; | ||
|
||
import com.intellij.execution.ExecutionListener; | ||
import com.intellij.execution.ExecutionManager; | ||
import com.intellij.execution.RunManagerListener; | ||
import com.intellij.execution.RunnerAndConfigurationSettings; | ||
import com.intellij.execution.process.ProcessHandler; | ||
import com.intellij.execution.runners.ExecutionEnvironment; | ||
import com.intellij.ide.DataManager; | ||
import com.intellij.openapi.Disposable; | ||
import com.intellij.openapi.actionSystem.*; | ||
import com.intellij.openapi.application.ReadAction; | ||
import com.intellij.openapi.module.Module; | ||
import com.intellij.openapi.module.ModuleManager; | ||
import com.intellij.openapi.project.DumbService; | ||
import com.intellij.openapi.project.Project; | ||
import com.intellij.openapi.ui.SimpleToolWindowPanel; | ||
import com.intellij.ui.AnimatedIcon; | ||
import com.intellij.ui.DoubleClickListener; | ||
import com.intellij.ui.treeStructure.Tree; | ||
import com.intellij.util.concurrency.AppExecutorUtil; | ||
import com.intellij.util.messages.MessageBusConnection; | ||
import com.redhat.devtools.intellij.quarkus.run.QuarkusRunConfiguration; | ||
import com.redhat.microprofile.psi.quarkus.PsiQuarkusUtils; | ||
import org.jetbrains.annotations.NotNull; | ||
import org.jetbrains.annotations.Nullable; | ||
|
||
import javax.swing.tree.DefaultMutableTreeNode; | ||
import javax.swing.tree.DefaultTreeModel; | ||
import javax.swing.tree.TreeNode; | ||
import javax.swing.tree.TreePath; | ||
import java.awt.event.KeyAdapter; | ||
import java.awt.event.KeyEvent; | ||
import java.awt.event.MouseEvent; | ||
import java.util.Enumeration; | ||
|
||
import static com.redhat.devtools.intellij.quarkus.run.QuarkusRunConfiguration.QUARKUS_CONFIGURATION; | ||
|
||
public class QuarkusExplorer extends SimpleToolWindowPanel implements Disposable { | ||
|
||
private final Tree tree; | ||
private final Project project; | ||
|
||
public QuarkusExplorer(@NotNull Project project) { | ||
super(true, true); | ||
this.project = project; | ||
tree = buildTree(); | ||
this.setContent(tree); | ||
load(); | ||
} | ||
|
||
/** | ||
* Builds the Language server tree | ||
* | ||
* @return Tree object of all language servers | ||
*/ | ||
private Tree buildTree() { | ||
|
||
DefaultMutableTreeNode top = new DefaultMutableTreeNode("Quarkus projects"); | ||
|
||
Tree tree = new Tree(top); | ||
tree.setRootVisible(false); | ||
tree.setCellRenderer(new QuarkusTreeRenderer()); | ||
|
||
tree.putClientProperty(AnimatedIcon.ANIMATION_IN_RENDERER_ALLOWED, true); | ||
|
||
((DefaultTreeModel) tree.getModel()).reload(top); | ||
|
||
|
||
var doubleClickListener = new DoubleClickListener() { | ||
@Override | ||
protected boolean onDoubleClick(@NotNull MouseEvent event) { | ||
executeAction(tree); | ||
return false; | ||
} | ||
}; | ||
doubleClickListener.installOn(tree); | ||
|
||
tree.addKeyListener(new KeyAdapter() { | ||
@Override | ||
public void keyPressed(KeyEvent e) { | ||
if (e.getKeyCode() == KeyEvent.VK_ENTER) { | ||
executeAction(tree); | ||
} | ||
} | ||
}); | ||
|
||
MessageBusConnection connection = project.getMessageBus().connect(project); | ||
connection.subscribe(RunManagerListener.TOPIC, new RunManagerListener() { | ||
@Override | ||
public void runConfigurationSelected(@Nullable RunnerAndConfigurationSettings settings) { | ||
// Do nothing | ||
} | ||
|
||
@Override | ||
public void runConfigurationAdded(@NotNull RunnerAndConfigurationSettings settings) { | ||
// TODO: refresh tree | ||
} | ||
}); | ||
connection.subscribe(ExecutionManager.EXECUTION_TOPIC, new ExecutionListener() { | ||
|
||
@Override | ||
public void processNotStarted(@NotNull String executorId, @NotNull ExecutionEnvironment env) { | ||
QuarkusRunDevNode application = findQuarkusApplication(env); | ||
if (application != null) { | ||
application.setApplicationStatus(QuarkusRunDevNode.QuarkusApplicationStatus.stopped); | ||
} | ||
} | ||
|
||
@Override | ||
public void processStarted(@NotNull String executorId, @NotNull ExecutionEnvironment env, final @NotNull ProcessHandler handler) { | ||
QuarkusRunDevNode application = findQuarkusApplication(env); | ||
if (application != null) { | ||
application.setApplicationStatus(QuarkusRunDevNode.QuarkusApplicationStatus.starting); | ||
} | ||
} | ||
|
||
@Override | ||
public void processTerminating(@NotNull String executorId, @NotNull ExecutionEnvironment env, @NotNull ProcessHandler handler) { | ||
QuarkusRunDevNode application = findQuarkusApplication(env); | ||
if (application != null) { | ||
application.setApplicationStatus(QuarkusRunDevNode.QuarkusApplicationStatus.stopping); | ||
} | ||
} | ||
|
||
@Override | ||
public void processTerminated(@NotNull String executorId, @NotNull ExecutionEnvironment env, @NotNull ProcessHandler handler, int exitCode) { | ||
QuarkusRunDevNode application = findQuarkusApplication(env); | ||
if (application != null) { | ||
application.setApplicationStatus(QuarkusRunDevNode.QuarkusApplicationStatus.stopped); | ||
} | ||
} | ||
|
||
private @Nullable QuarkusRunDevNode findQuarkusApplication(@NotNull ExecutionEnvironment env) { | ||
QuarkusRunConfiguration runConfiguration = env.getDataContext() != null ? (QuarkusRunConfiguration) env.getDataContext().getData(QUARKUS_CONFIGURATION) : null; | ||
if (runConfiguration == null) { | ||
return null; | ||
} | ||
|
||
Module module = runConfiguration.getModule(); | ||
DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree.getModel().getRoot(); | ||
Enumeration<TreeNode> children = root.children(); | ||
while (children.hasMoreElements()) { | ||
TreeNode node = children.nextElement(); | ||
if (node instanceof QuarkusProjectNode && module.equals(((QuarkusProjectNode) node).getModule())) { | ||
QuarkusProjectNode project = (QuarkusProjectNode) node; | ||
Enumeration<TreeNode> children2 = project.children(); | ||
while (children2.hasMoreElements()) { | ||
TreeNode node2 = children2.nextElement(); | ||
if (node2 instanceof QuarkusRunDevNode && runConfiguration.equals(((QuarkusRunDevNode) node2).getConfiguration())) { | ||
return (QuarkusRunDevNode) node2; | ||
} | ||
} | ||
break; | ||
} | ||
} | ||
return null; | ||
} | ||
}); | ||
return tree; | ||
} | ||
|
||
private void load() { | ||
var action = ReadAction.nonBlocking(() -> { | ||
DefaultMutableTreeNode root = (DefaultMutableTreeNode) ((DefaultTreeModel) tree.getModel()).getRoot(); | ||
Module[] modules = ModuleManager.getInstance(project).getModules(); | ||
for (Module javaProject : modules) { | ||
if (PsiQuarkusUtils.isQuarkusProject(javaProject)) { | ||
QuarkusProjectNode projectNode = new QuarkusProjectNode(javaProject, tree); | ||
root.add(projectNode); | ||
// Fill Quarkus actions | ||
projectNode.add(new QuarkusRunDevNode(projectNode)); | ||
} | ||
} | ||
((DefaultTreeModel) tree.getModel()).reload(root); | ||
}); | ||
var executeInSmartMode = DumbService.getInstance(project).isDumb(); | ||
if (executeInSmartMode) { | ||
action = action.inSmartMode(project); | ||
} | ||
action | ||
.submit(AppExecutorUtil.getAppExecutorService()); | ||
} | ||
|
||
private static void executeAction(Tree tree) { | ||
final TreePath path = tree.getSelectionPath(); | ||
Object node = path.getLastPathComponent(); | ||
if (node instanceof QuarkusActionNode) { | ||
ActionManager am = ActionManager.getInstance(); | ||
String actionId = ((QuarkusActionNode) node).getActionId(); | ||
if (actionId == null) { | ||
return; | ||
} | ||
AnAction action = am.getAction(actionId); | ||
if (action != null) { | ||
action.actionPerformed(new AnActionEvent(null, | ||
DataManager.getInstance().getDataContext(tree), | ||
ActionPlaces.UNKNOWN, new Presentation(), | ||
am, 0)); | ||
} | ||
|
||
} | ||
} | ||
|
||
@Override | ||
public void dispose() { | ||
|
||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
src/main/java/com/redhat/devtools/intellij/quarkus/explorer/QuarkusOpenApplicationNode.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,15 @@ | ||
package com.redhat.devtools.intellij.quarkus.explorer; | ||
|
||
import com.redhat.devtools.intellij.quarkus.run.QuarkusOpenAppInBrowserAction; | ||
import com.redhat.devtools.intellij.quarkus.run.QuarkusOpenDevUIAction; | ||
|
||
public class QuarkusOpenApplicationNode extends QuarkusActionNode { | ||
public QuarkusOpenApplicationNode(QuarkusProjectNode projectNode) { | ||
super("Open Application", projectNode); | ||
} | ||
|
||
@Override | ||
public String getActionId() { | ||
return QuarkusOpenAppInBrowserAction.ACTION_ID; | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
src/main/java/com/redhat/devtools/intellij/quarkus/explorer/QuarkusOpenDevUINode.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,18 @@ | ||
package com.redhat.devtools.intellij.quarkus.explorer; | ||
|
||
import com.intellij.openapi.module.Module; | ||
import com.intellij.ui.treeStructure.Tree; | ||
import com.redhat.devtools.intellij.quarkus.run.QuarkusOpenDevUIAction; | ||
|
||
import javax.swing.tree.DefaultTreeModel; | ||
|
||
public class QuarkusOpenDevUINode extends QuarkusActionNode { | ||
public QuarkusOpenDevUINode(QuarkusProjectNode projectNode) { | ||
super("Open Dev UI", projectNode); | ||
} | ||
|
||
@Override | ||
public String getActionId() { | ||
return QuarkusOpenDevUIAction.ACTION_ID; | ||
} | ||
} |
Oops, something went wrong.