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 bddf7fe
Show file tree
Hide file tree
Showing 9 changed files with 504 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,212 @@
/*******************************************************************************
* 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.ProjectTopics;
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.externalSystem.autoimport.ExternalSystemProjectListener;
import com.intellij.openapi.externalSystem.service.project.ProjectDataManager;
import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataImportListener;
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.ModuleListener;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.concurrency.AppExecutorUtil;
import com.intellij.util.containers.ContainerUtil;
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 org.jetbrains.plugins.gradle.util.GradleConstants;

import java.util.*;

/**
* 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).isCreateQuarkusRunConfigurationOnProjectImport()) {
return;
}

VirtualFile projectDir = ProjectUtil.guessProjectDir(project);
assert projectDir != null;
VirtualFile[] children = projectDir.getChildren();
boolean isGradleProject = ContainerUtil.exists(children, file -> GradleConstants.KNOWN_GRADLE_FILES.contains(file.getName()));

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) {
projectConnection.subscribe(ProjectDataImportListener.TOPIC, new ProjectDataImportListener() {

@Override
public void onImportFinished(@Nullable String projectPath) {
if (!UserDefinedQuarkusSettings.getInstance(project).isCreateQuarkusRunConfigurationOnProjectImport()) {
return;
}
List<Module> modules = new ArrayList<>();
Module[] existingModules = ModuleManager.getInstance(project).getModules();
for (Module module : existingModules) {
// Check if the module is a Gradle project
if (ExternalSystemApiUtil.isExternalSystemAwareModule(GradleConstants.SYSTEM_ID, module)) {
modules.add(module);
}
}
tryToCreateRunConfigurations(modules);
}
});
}

private void tryToCreateRunConfigurations(List<Module> modules) {
if (modules.isEmpty()) {
return;
}
ReadAction.nonBlocking(() -> doTryToCreateRunConfigurations(modules))
.inSmartMode(project)
.submit(AppExecutorUtil.getAppExecutorService());
}

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

private boolean tryToCreateRunConfiguration(Module module) {
if (!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,85 @@
/*******************************************************************************
* 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.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.setCreateQuarkusRunConfigurationOnProjectImport(settings.isCreateQuarkusRunConfigurationOnProjectImport());
}

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

@Override
public void apply() throws ConfigurationException {
if (myView == null) return;
UserDefinedQuarkusSettings settings = UserDefinedQuarkusSettings.getInstance(project);
settings.setCreateQuarkusRunConfigurationOnProjectImport(myView.isCreateQuarkusRunConfigurationOnProjectImport());
settings.fireStateChanged();
}
}
Loading

0 comments on commit bddf7fe

Please sign in to comment.