Skip to content

Commit

Permalink
feat: Auto create Quarkus run config while importing project
Browse files Browse the repository at this point in the history
Fixes #1233

Signed-off-by: azerr <[email protected]>
  • Loading branch information
angelozerr committed Nov 10, 2023
1 parent 95f8798 commit 54d956e
Show file tree
Hide file tree
Showing 9 changed files with 463 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@
import com.intellij.openapi.startup.StartupActivity;
import com.redhat.devtools.intellij.lsp4mp4ij.classpath.ClasspathResourceChangedManager;
import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.project.PsiMicroProfileProjectManager;
import com.redhat.devtools.intellij.quarkus.run.QuarkusRunConfigurationManager;
import org.jetbrains.annotations.NotNull;

public class QuarkusPostStartupActivity implements StartupActivity, DumbAware {
@Override
public void runActivity(@NotNull Project project) {
QuarkusRunConfigurationManager.getInstance(project);
ClasspathResourceChangedManager.getInstance(project);
// Force the instantiation of the manager to be sure that classpath listener
// are registered before QuarkusLanguageClient classpath listener
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/*******************************************************************************
* 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.run;

import com.intellij.execution.RunManager;
import com.intellij.execution.RunnerAndConfigurationSettings;
import com.intellij.execution.dashboard.RunDashboardManager;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.util.messages.MessageBusConnection;
import com.redhat.devtools.intellij.quarkus.settings.UserDefinedQuarkusSettings;
import com.redhat.microprofile.psi.quarkus.PsiQuarkusUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.maven.MavenDisposable;
import org.jetbrains.idea.maven.project.MavenImportListener;
import org.jetbrains.idea.maven.project.MavenProject;
import org.jetbrains.idea.maven.project.MavenProjectsManager;

import java.util.*;
import java.util.stream.Collectors;

/**
* Quarkus run configuration manager used to:
*
* <ul>
* <li>auto create Quarkus run configuration when a Quarkus Maven/Gradle is imported</li>
* <li>provides methods to find existing/ create Quarkus run configuration from a given module</li>
* </ul>
*/
public class QuarkusRunConfigurationManager implements Disposable {

public static QuarkusRunConfigurationManager getInstance(Project project) {
return ServiceManager.getService(project, QuarkusRunConfigurationManager.class);
}

private final Project project;
private final MessageBusConnection projectConnection;

public QuarkusRunConfigurationManager(Project project) {
this.project = project;
projectConnection = initializeAutoCreateRunConfigurationWhileImporting(project);
}

public @Nullable RunnerAndConfigurationSettings findExistingConfigurationFor(@NotNull Module module) {
List<RunnerAndConfigurationSettings> configurations = RunManager.getInstance(project).getConfigurationSettingsList(QuarkusRunConfigurationType.class);
if (!configurations.isEmpty()) {
for (RunnerAndConfigurationSettings settings : configurations) {
QuarkusRunConfiguration configuration = (QuarkusRunConfiguration) settings.getConfiguration();
if (module.equals(configuration.getModule())) {
return settings;
}
}
}
return null;
}

public @NotNull RunnerAndConfigurationSettings createConfiguration(@NotNull Module module, boolean save) {
var runManager = RunManager.getInstance(module.getProject());
RunnerAndConfigurationSettings quarkusSettings = runManager.createConfiguration(generateConfigurationName(module, runManager), QuarkusRunConfigurationType.class);
((QuarkusRunConfiguration) quarkusSettings.getConfiguration()).setModule(module);
if (save) {
quarkusSettings.storeInLocalWorkspace();
// Save the configuration
runManager.addConfiguration(quarkusSettings);
}
return quarkusSettings;
}

@NotNull
private static String generateConfigurationName(@NotNull Module module, RunManager runManager) {
String configurationName = module.getName();
RunnerAndConfigurationSettings existingConfiguration = runManager.findConfigurationByTypeAndName(QuarkusRunConfigurationType.ID, configurationName);
int index = 0;
while(existingConfiguration != null) {
configurationName = module.getName() + " (" + index++ + ")";
existingConfiguration = runManager.findConfigurationByTypeAndName(QuarkusRunConfigurationType.ID, configurationName);
}
return configurationName;
}

@NotNull
private MessageBusConnection initializeAutoCreateRunConfigurationWhileImporting(Project project) {
MessageBusConnection projectConnection = project.getMessageBus().connect(MavenDisposable.getInstance(project));
autoCreateWithMaven(project, projectConnection);
autoCreateWithGradle(project, projectConnection);
return projectConnection;
}

private void autoCreateWithMaven(Project project, MessageBusConnection projectConnection) {
projectConnection.subscribe(MavenImportListener.TOPIC, new MavenImportListener() {
@Override
public void importFinished(@NotNull Collection<MavenProject> importedProjects, @NotNull List<Module> newModules) {
if (!UserDefinedQuarkusSettings.getInstance(project).isCreateQuarkusRunConfigurationWhileProjectImport()) {
return;
}
List<Module> modules = new ArrayList<>();
for (MavenProject mavenProject : importedProjects) {
MavenProjectsManager projectsManager = MavenProjectsManager.getInstance(project);
Module module = projectsManager.findModule(mavenProject);
if (module != null) {
modules.add(module);
}
}
tryToCreateRunConfigurations(modules);
}
});
}

private void autoCreateWithGradle(Project project, MessageBusConnection projectConnection) {
// TODO : track the Gradle import projects
}

private void tryToCreateRunConfigurations(List<Module> modules) {
if (modules.isEmpty()) {
return;
}
boolean runConfigurationCreated = false;
for (Module module: modules) {
if (tryToCreateRunConfiguration(module)) {
runConfigurationCreated = true;
}
}
if (runConfigurationCreated) {
addQuarkusRunConfigurationTypeInServicesViewIfNeeded();
}
}

private boolean tryToCreateRunConfiguration(Module module) {
if (!ReadAction.compute(()-> PsiQuarkusUtils.isQuarkusProject(module))) {
return false;
}
// Find existing Quarkus run configuration
RunnerAndConfigurationSettings quarkusSettings = findExistingConfigurationFor(module);
if (quarkusSettings == null) {
// No Quarkus run configuration for the module, create it and save it in the .idea/workspace.xml file
createConfiguration(module, true);
return true;
}
return false;
}

private void addQuarkusRunConfigurationTypeInServicesViewIfNeeded() {
RunDashboardManager runDashboardManager = RunDashboardManager.getInstance(project);
Set<String> types = new HashSet<>(runDashboardManager.getTypes());
if (!types.contains(QuarkusRunConfigurationType.ID)) {
types.add(QuarkusRunConfigurationType.ID);
runDashboardManager.setTypes(types);
}
}

@Override
public void dispose() {
projectConnection.disconnect();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import javax.swing.Icon;

public class QuarkusRunConfigurationType implements ConfigurationType {
private static final String ID = "QuarkusRunConfiguration";
public static final String ID = "QuarkusRunConfiguration";

@NotNull
@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*******************************************************************************
* 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.settings;

import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.NamedConfigurable;
import com.intellij.openapi.util.NlsContexts;
import com.redhat.devtools.intellij.lsp4mp4ij.settings.UserDefinedMicroProfileSettings;
import com.redhat.devtools.intellij.quarkus.QuarkusBundle;

import javax.swing.*;

/**
* Quarkus configuration.
*/
public class QuarkusConfigurable extends NamedConfigurable<UserDefinedQuarkusSettings> {

private final Project project;
private QuarkusView myView;

public QuarkusConfigurable(Project project) {
this.project = project;
}

@Override
public UserDefinedQuarkusSettings getEditableObject() {
return null;
}

@Override
public @NlsContexts.DetailedDescription String getBannerSlogan() {
return null;
}

@Override
public JComponent createOptionsPanel() {
if (myView == null) {
myView = new QuarkusView();
}
return myView.getComponent();
}

@Override
public void setDisplayName(String name) {
}

@Override
public @NlsContexts.ConfigurableName String getDisplayName() {
return QuarkusBundle.message("quarkus");
}


@Override
public void reset() {
if (myView == null) return;
UserDefinedQuarkusSettings settings = UserDefinedQuarkusSettings.getInstance(project);
myView.setCreateQuarkusRunConfigurationWhileProjectImport(settings.isCreateQuarkusRunConfigurationWhileProjectImport());
}

@Override
public boolean isModified() {
if (myView == null) return false;
UserDefinedQuarkusSettings settings = UserDefinedQuarkusSettings.getInstance(project);
return !(myView.isCreateQuarkusRunConfigurationWhileProjectImport() == settings.isCreateQuarkusRunConfigurationWhileProjectImport());
}

@Override
public void apply() throws ConfigurationException {
if (myView == null) return;
UserDefinedQuarkusSettings settings = UserDefinedQuarkusSettings.getInstance(project);
settings.setCreateQuarkusRunConfigurationWhileImport(myView.isCreateQuarkusRunConfigurationWhileProjectImport());
settings.fireStateChanged();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*******************************************************************************
* 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.settings;

import com.intellij.openapi.Disposable;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.util.ui.FormBuilder;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.UI;
import com.redhat.devtools.intellij.quarkus.QuarkusBundle;

import javax.swing.*;
import javax.swing.border.TitledBorder;

/**
* Quarkus view.
*/
public class QuarkusView implements Disposable {

private final JPanel myMainPanel;

private JBCheckBox createQuarkusRunConfigurationWhileProjectImportCheckBox = new JBCheckBox(QuarkusBundle.message("quarkus.create.quarkus.run.configuration.while.project.import"));

public QuarkusView() {
JComponent descriptionPanel = createDescription(null);
JPanel settingsPanel = createSettings(descriptionPanel);
TitledBorder title = IdeBorderFactory.createTitledBorder(QuarkusBundle.message("quarkus.title"));
settingsPanel.setBorder(title);
this.myMainPanel = JBUI.Panels.simplePanel(10, 10)
.addToLeft(JBUI.Panels.simplePanel())
.addToCenter(settingsPanel);
}

private JPanel createSettings(JComponent description) {
return FormBuilder.createFormBuilder()
.addComponent(description, 0)
.addComponent(createQuarkusRunConfigurationWhileProjectImportCheckBox, 5)
.addComponentFillVertically(new JPanel(), 0)
.getPanel();
}

private JComponent createDescription(String description) {
/**
* Normally comments are below the controls.
* Here we want the comments to precede the controls, we therefore create an empty, 0-sized panel.
*/
JPanel titledComponent = UI.PanelFactory.grid().createPanel();
titledComponent.setMinimumSize(JBUI.emptySize());
titledComponent.setPreferredSize(JBUI.emptySize());
if (description != null && !description.isBlank()) {
titledComponent = UI.PanelFactory.panel(titledComponent)
.withComment(description)
.resizeX(true)
.resizeY(true)
.createPanel();
}
return titledComponent;
}

public JComponent getComponent() {
return myMainPanel;
}


public boolean isCreateQuarkusRunConfigurationWhileProjectImport() {
return createQuarkusRunConfigurationWhileProjectImportCheckBox.isSelected();
}

public void setCreateQuarkusRunConfigurationWhileProjectImport(boolean createQuarkusRunConfigurationWhileProjectImport) {
createQuarkusRunConfigurationWhileProjectImportCheckBox.setSelected(createQuarkusRunConfigurationWhileProjectImport);
}

@Override
public void dispose() {

}
}
Loading

0 comments on commit 54d956e

Please sign in to comment.