diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index b51dd53..78e711c 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,8 +1,12 @@ + \ No newline at end of file diff --git a/build.gradle b/build.gradle index 38aa79c..0c024ee 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { - id 'org.jetbrains.intellij' version '1.17.3' - id 'java' + id 'org.jetbrains.intellij' version '1.17.3' + id 'java' id "org.jsonschema2pojo" version "1.2.1" } @@ -17,33 +17,33 @@ allprojects { jUnitJupiterVersion = '5.10.1' intellijType = 'IC' // IC: Community Edition, IU: Ultimate Edition. intellijVersion = '2024.1' // https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html - intellijSinceBuild = '231.1' + intellijSinceBuild = '232.1' } } repositories { - mavenCentral() + mavenCentral() } // Must match Groovy version bundled with IntelliJ (see IJ/Contents/lib/groovy.jar/META-INF/org.codehaus.groovy.runtime.ExtensionModule) ext.groovyVersion = "3.0.19" intellij { - // Available IDE versions: https://www.jetbrains.com/intellij-repository/releases and https://www.jetbrains.com/intellij-repository/snapshots - version = System.getenv().getOrDefault("IJ_VERSION", - "2024.1" - ) - pluginName = "AutoConfigPlugin" - downloadSources = true - updateSinceUntilBuild = false - instrumentCode = false - plugins = [ - "java", - "Git4Idea", - "junit", - "org.jetbrains.plugins.yaml", - "org.jetbrains.idea.maven" - ] + // Available IDE versions: https://www.jetbrains.com/intellij-repository/releases and https://www.jetbrains.com/intellij-repository/snapshots + version = System.getenv().getOrDefault("IJ_VERSION", + "2024.1" + ) + pluginName = "AutoConfigPlugin" + downloadSources = true + updateSinceUntilBuild = false + instrumentCode = false + plugins = [ + "java", + "Git4Idea", + "junit", + "org.jetbrains.plugins.yaml", + "org.jetbrains.idea.maven" + ] } buildSearchableOptions.enabled = false // Disable because it takes a long time and the plugin doesn't need it runIde.jbrVariant.set("jcef") // JVM with Java Chromium Embedded Framework because why not (see also https://github.com/JetBrains/gradle-intellij-plugin#running-dsl) @@ -271,18 +271,18 @@ jsonSchema2Pojo { } sourceSets { - // Keep Groovy and Kotlin API source code in separate source sets, otherwise - // compilation fails because of inter-dependencies between Kotlin and Groovy files which confuse compiler, - // even though overall dependencies are unidirectional: pluginApiKotlin -> pluginApiGroovy -> main. - - main { - java { srcDir "src/main" } - resources { srcDir "resources" } - } + // Keep Groovy and Kotlin API source code in separate source sets, otherwise + // compilation fails because of inter-dependencies between Kotlin and Groovy files which confuse compiler, + // even though overall dependencies are unidirectional: pluginApiKotlin -> pluginApiGroovy -> main. + + main { + java { srcDir "src/main" } + resources { srcDir "resources" } + } } compileJava { - sourceCompatibility = "17" - targetCompatibility = "17" + sourceCompatibility = "17" + targetCompatibility = "17" } diff --git a/src/main/java/de/gebit/intellij/autoconfig/AutoconfigStartup.java b/src/main/java/de/gebit/intellij/autoconfig/AutoconfigStartup.java index 6360835..42370dd 100644 --- a/src/main/java/de/gebit/intellij/autoconfig/AutoconfigStartup.java +++ b/src/main/java/de/gebit/intellij/autoconfig/AutoconfigStartup.java @@ -24,35 +24,35 @@ import static de.gebit.intellij.autoconfig.util.Notifications.showInfo; /** - * Entry point for the opening of a project. The yaml configuration file is read here, the resulting configuration options object is passed to the CommonConfigurationHandler. - * At the end, a message is composed, displaying a list of all updated configuration options. + * Entry point for the opening of a project. The yaml configuration file is read here, the resulting configuration options object is passed to the + * CommonConfigurationHandler. At the end, a message is composed, displaying a list of all updated configuration options. */ public class AutoconfigStartup implements ProjectActivity { - private static final com.intellij.openapi.diagnostic.Logger LOG = Logger.getInstance(AutoconfigStartup.class); + private static final com.intellij.openapi.diagnostic.Logger LOG = Logger.getInstance(AutoconfigStartup.class); - public static final ExtensionPointName> - EP_NAME = ExtensionPointName.create("de.gebit.intellij.autoconfig.configurationUpdater"); + public static final ExtensionPointName> + EP_NAME = ExtensionPointName.create("de.gebit.intellij.autoconfig.configurationUpdater"); - @Nullable - @Override - public Object execute(@NotNull Project project, @NotNull Continuation continuation) { - ConfigurationLoaderService projectService = project.getService(ConfigurationLoaderService.class); - if (projectService == null) { - return null; - } + @Nullable + @Override + public Object execute(@NotNull Project project, @NotNull Continuation continuation) { + ConfigurationLoaderService projectService = project.getService(ConfigurationLoaderService.class); + if (projectService == null) { + return null; + } - List changedConfigs = new ArrayList<>(); + List changedConfigs = new ArrayList<>(); - for (UpdateHandler updateHandler : EP_NAME.getExtensionList()) { - Optional extensionConfiguration = projectService.getConfiguration(updateHandler.getConfigurationClass(), updateHandler.getFileName()); - extensionConfiguration.ifPresentOrElse(config -> changedConfigs.addAll(updateHandler.updateConfiguration(config, project)), () -> LOG.info("No configuration for " + updateHandler.getUpdaterName() + " found.")); - } + for (UpdateHandler updateHandler : EP_NAME.getExtensionList()) { + Optional extensionConfiguration = projectService.getConfiguration(updateHandler.getConfigurationClass(), updateHandler.getFileName()); + extensionConfiguration.ifPresentOrElse(config -> changedConfigs.addAll(updateHandler.updateConfiguration(config, project)), () -> LOG.info("No configuration for " + updateHandler.getUpdaterName() + " found.")); + } - if (!changedConfigs.isEmpty()) { - String notification = String.join(", ", changedConfigs); - showInfo("New project configurations applied: " + notification, project); - } + if (!changedConfigs.isEmpty()) { + String notification = String.join(", ", changedConfigs); + showInfo("New project configurations applied: " + notification, project); + } - return changedConfigs; - } + return changedConfigs; + } } diff --git a/src/main/java/de/gebit/intellij/autoconfig/ConfigurationLoaderService.java b/src/main/java/de/gebit/intellij/autoconfig/ConfigurationLoaderService.java index 038c2c6..7b2807f 100644 --- a/src/main/java/de/gebit/intellij/autoconfig/ConfigurationLoaderService.java +++ b/src/main/java/de/gebit/intellij/autoconfig/ConfigurationLoaderService.java @@ -38,6 +38,7 @@ public Optional getConfiguration(Class objectClass, String configFileNa T configObject = null; Object o = configurationOptions.get(objectClass.getCanonicalName()); if (o != null && o.getClass().isAssignableFrom(objectClass)) { + //noinspection unchecked configObject = (T) o; } if (configObject != null) { diff --git a/src/main/java/de/gebit/intellij/autoconfig/handlers/CommonConfigurationHandler.java b/src/main/java/de/gebit/intellij/autoconfig/handlers/CommonConfigurationHandler.java index 3f6d010..8711807 100644 --- a/src/main/java/de/gebit/intellij/autoconfig/handlers/CommonConfigurationHandler.java +++ b/src/main/java/de/gebit/intellij/autoconfig/handlers/CommonConfigurationHandler.java @@ -13,15 +13,13 @@ import com.intellij.codeInsight.actions.onSave.OptimizeImportsOnSaveOptions; import com.intellij.externalDependencies.ExternalDependenciesManager; import com.intellij.externalDependencies.impl.ExternalDependenciesManagerImpl; +import com.intellij.openapi.externalSystem.autoimport.ExternalSystemProjectTrackerSettings; import com.intellij.openapi.project.Project; import com.intellij.openapi.updateSettings.impl.UpdateSettings; import com.intellij.openapi.vcs.IssueNavigationConfiguration; import com.intellij.openapi.vcs.IssueNavigationLink; import de.gebit.intellij.autoconfig.UpdateHandler; -import de.gebit.intellij.autoconfig.model.Formatting; -import de.gebit.intellij.autoconfig.model.GeneralConfiguration; -import de.gebit.intellij.autoconfig.model.IssueNavigation; -import de.gebit.intellij.autoconfig.model.OnSave; +import de.gebit.intellij.autoconfig.model.*; import de.gebit.intellij.autoconfig.state.TransientPluginStateService; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.Nullable; @@ -38,6 +36,8 @@ public class CommonConfigurationHandler extends AbstractHandler implements Updat public static final @NonNls String CONFIG_FILE_NAME = "autoconfig.yaml"; + public static final @NonNls Class CONFIGURATION_CLASS = GeneralConfiguration.class; + @Override public String getFileName() { return CONFIG_FILE_NAME; @@ -55,18 +55,34 @@ public String getUpdaterName() { @Override public Class getConfigurationClass() { - return GeneralConfiguration.class; + return CONFIGURATION_CLASS; } @Override public List updateConfiguration(GeneralConfiguration options, Project project) { List updatedConfigs = new ArrayList<>(); applyIssueNavigationConfiguration(options.getIssueNavigation(), project, updatedConfigs); - applyPluginHosts(options.getPluginRepositories(), project, updatedConfigs); + applyPluginHosts(options.getGlobalPluginRepositories(), project, updatedConfigs); applyOnSaveOptions(options.getOnSave(), project, updatedConfigs); + applyReloadConfiguration(options.getReloadProjectAutomatically(), project, updatedConfigs); return updatedConfigs; } + private void applyReloadConfiguration(ReloadProjectAutomatically reloadProjectAutomatically, Project project, List updatedConfigs) { + if (reloadProjectAutomatically != null) { + final ExternalSystemProjectTrackerSettings instance = ExternalSystemProjectTrackerSettings.getInstance(project); + if (Boolean.FALSE.equals(reloadProjectAutomatically.getEnabled())) { + applySetting(ExternalSystemProjectTrackerSettings.AutoReloadType.NONE, instance.getAutoReloadType(), instance::setAutoReloadType, updatedConfigs, "Automatic project reload deactivated"); + instance.setAutoReloadType(ExternalSystemProjectTrackerSettings.AutoReloadType.NONE); + } else if (reloadProjectAutomatically.getMode() != null) { + applySetting(switch (reloadProjectAutomatically.getMode()) { + case ANY_CHANGES -> ExternalSystemProjectTrackerSettings.AutoReloadType.ALL; + case EXTERNAL_CHANGES -> ExternalSystemProjectTrackerSettings.AutoReloadType.SELECTIVE; + }, instance.getAutoReloadType(), instance::setAutoReloadType, updatedConfigs, "Automatic project reload activated"); + } + } + } + private void applyIssueNavigationConfiguration(List issueNavigationConfig, Project aProject, List updatedConfigs) { if (issueNavigationConfig != null) { var issueNavigationSettings = IssueNavigationConfiguration.getInstance(aProject); @@ -103,6 +119,7 @@ private List getFileTypes(@Nullable Formatting formattingOptions) { } private void applyPluginHosts(List pluginHosts, Project aProject, List changedConfigs) { + // Maybe ask the user before applying new Plugin-Hosts? This could be a possible attack vector otherwise. if (pluginHosts != null) { var updateSettings = UpdateSettings.getInstance(); var storedPluginHosts = updateSettings.getStoredPluginHosts(); diff --git a/src/main/java/de/gebit/intellij/autoconfig/plugins/AutoConfigUpdateSettingsProvider.java b/src/main/java/de/gebit/intellij/autoconfig/plugins/AutoConfigUpdateSettingsProvider.java new file mode 100644 index 0000000..26320f6 --- /dev/null +++ b/src/main/java/de/gebit/intellij/autoconfig/plugins/AutoConfigUpdateSettingsProvider.java @@ -0,0 +1,30 @@ +package de.gebit.intellij.autoconfig.plugins; + +import com.intellij.openapi.updateSettings.impl.UpdateSettingsProvider; +import de.gebit.intellij.autoconfig.ConfigurationLoaderService; +import de.gebit.intellij.autoconfig.handlers.CommonConfigurationHandler; +import de.gebit.intellij.autoconfig.model.GeneralConfiguration; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * "Safer" Plugin-Repository Provider which does not persist the configured plugin repository. This provider is used when checking for required + * plugins and when checking for plugin updates. + */ +public class AutoConfigUpdateSettingsProvider implements UpdateSettingsProvider { + + @NotNull + @Override + public List getPluginRepositories() { + List pluginRepositories = new ArrayList<>(); + com.intellij.openapi.project.ProjectUtil.getOpenedProjects().iterator().forEachRemaining(project -> { + ConfigurationLoaderService projectService = project.getService(ConfigurationLoaderService.class); + final Optional configuration = projectService.getConfiguration(CommonConfigurationHandler.CONFIGURATION_CLASS, CommonConfigurationHandler.CONFIG_FILE_NAME); + configuration.map(GeneralConfiguration::getPluginRepositories).ifPresent(pluginRepositories::addAll); + }); + return pluginRepositories; + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index ffc5ab6..a879bcc 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -1,12 +1,10 @@ de.gebit.intellij.autoconfig Autoconfig - GEBIT Solutions GmbH + GEBIT Solutions GmbH - 0.0.0 - - GEBIT Solutions GmbH + 0.0.1 + @@ -33,6 +31,7 @@ + diff --git a/src/main/resources/schema/config.schema.json b/src/main/resources/schema/config.schema.json index b034928..5bc53b6 100644 --- a/src/main/resources/schema/config.schema.json +++ b/src/main/resources/schema/config.schema.json @@ -1,6 +1,6 @@ { "$id": "https://www.gebit.de/autoconfig-intellij-plugin/config.schema.json", - "$schema": "https://json-schema.org/draft/2020-12/schema", + "$schema": "http://json-schema.org/draft-07/schema#", "title": "General configuration", "definitions": { "formatting": { @@ -15,6 +15,13 @@ } } } + }, + "pluginRepositories": { + "type": "array", + "description": "List of additional plugin repositories.", + "items": { + "type": "string" + } } }, "type": "object", @@ -23,8 +30,12 @@ "type": "object", "description": "Actions on save settings.", "properties": { - "format": { "$ref": "#/definitions/formatting" }, - "optimizeImports": { "$ref": "#/definitions/formatting" } + "format": { + "$ref": "#/definitions/formatting" + }, + "optimizeImports": { + "$ref": "#/definitions/formatting" + } } }, "issueNavigation": { @@ -48,12 +59,40 @@ } } }, + "globalPluginRepositories": { + "description": "List of plugin repositories to be configured globally (cross project).", + "$ref": "#/definitions/pluginRepositories" + }, "pluginRepositories": { - "type": "array", - "description": "List of additional plugin repositories.", - "items": { - "type": "string" + "description": "List of plugin repositories to be used when this project is open.", + "$ref": "#/definitions/pluginRepositories" + }, + "reloadProjectAutomatically": { + "type": "object", + "description": "Automatically reload the project after the configuration has been changed.", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable automatic reload of the project" + }, + "mode": { + "type": "string", + "description": "Reload on any changes or only reload on external changes e.g. from version control?", + "enum": [ + "any-changes", + "external-changes" + ], + "default": "external-changes", + "x-intellij-enum-metadata": { + "any-changes": { + "description": "Reload on any changes on project configuration files" + }, + "external-changes": { + "description": "Reload on external changes on project configuration files e.g. from version control" + } + } + } } } } -} \ No newline at end of file +}