Skip to content

Commit

Permalink
feat: Create a Quarkus Dev Explorer in the IDE
Browse files Browse the repository at this point in the history
Fixes #1175

Signed-off-by: azerr <[email protected]>
  • Loading branch information
angelozerr committed Sep 26, 2023
1 parent 9c95979 commit 2ea8d22
Show file tree
Hide file tree
Showing 16 changed files with 733 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*******************************************************************************
* Copyright (c) 2023 Red Hat Inc. and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
* which is available at https://www.apache.org/licenses/LICENSE-2.0.
*
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*******************************************************************************/
package com.redhat.devtools.intellij.quarkus;

import com.intellij.DynamicBundle;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.PropertyKey;

import java.util.function.Supplier;

/**
* Quarkus messages bundle.
*/
public final class QuarkusBundle extends DynamicBundle {

@NonNls public static final String BUNDLE = "messages.QuarkusBundle";
private static final QuarkusBundle INSTANCE = new QuarkusBundle();

private QuarkusBundle() {
super(BUNDLE);
}

@NotNull
public static @Nls String message(@NotNull @PropertyKey(resourceBundle = BUNDLE) String key, Object @NotNull ... params) {
return INSTANCE.getMessage(key, params);
}

@NotNull
public static Supplier<@Nls String> messagePointer(@NotNull @PropertyKey(resourceBundle = BUNDLE) String key, Object @NotNull ... params) {
return INSTANCE.getLazyMessage(key, params);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.redhat.devtools.intellij.quarkus.explorer;

import com.intellij.icons.AllIcons;
import com.intellij.openapi.module.Module;

import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;

public class QuarkusActionNode extends DefaultMutableTreeNode {

private final String name;

private final Module module;

private final DefaultTreeModel treeModel;
public QuarkusActionNode(String name, Module module, DefaultTreeModel treeModel) {
this.name = name;
this.module = module;
this.treeModel = treeModel;
}

public String getName() {
return name;
}

public Module getModule() {
return module;
}

public String getDisplayName() {
return name;
}

public Icon getIcon() {
return AllIcons.Actions.InlayGear;
}

public void refreshNode() {
treeModel.nodeChanged(this);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
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.explorer.actions.QuarkusDevStartAction;
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 processStarting(@NotNull String executorId, @NotNull ExecutionEnvironment env, @NotNull ProcessHandler handler) {
QuarkusRunApplicationNode application = findQuarkusApplication(env);
if (application != null) {
application.setApplicationStatus(QuarkusRunApplicationNode.QuarkusApplicationStatus.starting);
}
}

@Override
public void processNotStarted(@NotNull String executorId, @NotNull ExecutionEnvironment env) {
QuarkusRunApplicationNode application = findQuarkusApplication(env);
if (application != null) {
application.setApplicationStatus(QuarkusRunApplicationNode.QuarkusApplicationStatus.stopped);
}
}

@Override
public void processStarted(@NotNull String executorId, @NotNull ExecutionEnvironment env, final @NotNull ProcessHandler handler) {
QuarkusRunApplicationNode application = findQuarkusApplication(env);
if (application != null) {
application.setApplicationStatus(QuarkusRunApplicationNode.QuarkusApplicationStatus.started);
}
}

@Override
public void processTerminating(@NotNull String executorId, @NotNull ExecutionEnvironment env, @NotNull ProcessHandler handler) {
QuarkusRunApplicationNode application = findQuarkusApplication(env);
if (application != null) {
application.setApplicationStatus(QuarkusRunApplicationNode.QuarkusApplicationStatus.stopping);
}
}

@Override
public void processTerminated(@NotNull String executorId, @NotNull ExecutionEnvironment env, @NotNull ProcessHandler handler, int exitCode) {
QuarkusRunApplicationNode application = findQuarkusApplication(env);
if (application != null) {
application.setApplicationStatus(QuarkusRunApplicationNode.QuarkusApplicationStatus.stopped);
}
}

private @Nullable QuarkusRunApplicationNode 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 QuarkusRunApplicationNode && runConfiguration.equals(((QuarkusRunApplicationNode) node2).getConfiguration())) {
return (QuarkusRunApplicationNode) 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);
root.add(projectNode);
// Fill Quarkus actions
projectNode.add(new QuarkusRunApplicationNode(javaProject, (DefaultTreeModel) tree.getModel()));
}
}
((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 actionNodeName = ((QuarkusActionNode) node).getName();

String actionId = QuarkusDevStartAction.ACTION_ID;

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() {

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.redhat.devtools.intellij.quarkus.explorer;

import com.intellij.openapi.module.Module;
import com.redhat.devtools.intellij.quarkus.lang.QuarkusIcons;

import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;

public class QuarkusProjectNode extends DefaultMutableTreeNode {

private final Module module;

public QuarkusProjectNode(Module module) {
this.module = module;
}

public String getDisplayName() {
return module.getName();
}

public Icon getIcon() {
return QuarkusIcons.Quarkus;
}

public Module getModule() {
return module;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.redhat.devtools.intellij.quarkus.explorer;

import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.openapi.module.Module;
import com.redhat.devtools.intellij.quarkus.run.QuarkusRunConfiguration;

import javax.swing.tree.DefaultTreeModel;

public class QuarkusRunApplicationNode extends QuarkusActionNode{

private RunConfiguration configuration;

public void setConfiguration(RunConfiguration configuration) {
this.configuration = configuration;
}

public RunConfiguration getConfiguration() {
return configuration;
}

public static enum QuarkusApplicationStatus {
none,
starting,
started,
stopping,
stopped;
}

private QuarkusApplicationStatus applicationStatus;

public QuarkusRunApplicationNode(Module module, DefaultTreeModel treeModel) {
super("Run...", module, treeModel);
this.applicationStatus = QuarkusApplicationStatus.none;
}

public void setApplicationStatus(QuarkusApplicationStatus applicationStatus) {
this.applicationStatus = applicationStatus;
refreshNode();
}

@Override
public String getDisplayName() {
return super.getDisplayName() + " [" + applicationStatus + "]";
}
}
Loading

0 comments on commit 2ea8d22

Please sign in to comment.