configuration file — move getOptionsClass() to ConfigurationFactory idea/183.1860
authorVladimir Krivosheev <vladimir.krivosheev@jetbrains.com>
Sat, 25 Aug 2018 11:12:43 +0000 (13:12 +0200)
committerVladimir Krivosheev <vladimir.krivosheev@jetbrains.com>
Sat, 25 Aug 2018 11:17:13 +0000 (13:17 +0200)
29 files changed:
.idea/modules.xml
java/execution/impl/src/com/intellij/execution/applet/AppletConfiguration.java
java/execution/impl/src/com/intellij/execution/applet/AppletConfigurationType.java
java/execution/impl/src/com/intellij/execution/application/ApplicationConfiguration.java
java/execution/impl/src/com/intellij/execution/application/ApplicationConfigurationOptions.kt
java/execution/impl/src/com/intellij/execution/application/ApplicationConfigurationType.java
java/execution/impl/src/com/intellij/execution/remote/RemoteConfigurationType.java
java/execution/impl/src/com/intellij/execution/scratch/JavaScratchConfiguration.java
java/execution/impl/src/com/intellij/execution/scratch/JavaScratchConfigurationType.java
platform/lang-api/src/com/intellij/execution/configurations/ConfigurationFactory.java
platform/lang-api/src/com/intellij/execution/configurations/ConfigurationType.java
platform/lang-api/src/com/intellij/execution/configurations/LocatableConfigurationBase.java
platform/lang-api/src/com/intellij/execution/configurations/ModuleBasedConfiguration.java
platform/lang-api/src/com/intellij/execution/configurations/RunConfigurationBase.java
platform/projectModel-api/src/com/intellij/configurationStore/properties/CollectionStoredProperty.kt
platform/projectModel-api/src/com/intellij/configurationStore/properties/FloatStoredProperty.kt
platform/projectModel-api/src/com/intellij/configurationStore/properties/IntStoredProperty.kt
platform/projectModel-api/src/com/intellij/configurationStore/properties/LongStoredProperty.kt
platform/projectModel-api/src/com/intellij/configurationStore/properties/ObjectStoredProperty.kt
platform/projectModel-api/src/com/intellij/configurationStore/properties/StringStoredProperty.kt
platform/projectModel-api/src/com/intellij/openapi/components/BaseState.kt
platform/projectModel-api/src/com/intellij/openapi/components/StoredPropertyBase.kt
plugins/configuration-script/intellij.configurationScript.iml [new file with mode: 0644]
plugins/configuration-script/resources/META-INF/plugin.xml [new file with mode: 0644]
plugins/configuration-script/src/com/intellij/configurationScript/IdeConfigurationJsonSchemaProviderFactory.kt [new file with mode: 0644]
plugins/configuration-script/src/com/intellij/configurationScript/runConfigurations.kt [new file with mode: 0644]
plugins/configuration-script/test/ConfigurationFileTest.kt [new file with mode: 0644]
plugins/devkit/devkit-core/src/run/PluginConfigurationType.java
plugins/junit/src/com/intellij/execution/junit/JUnitConfigurationType.java

index 3bc63772d9470bcb02b32b8ff7df5857d55c4e5b..c586059e3cebce27141e5bc14f2c81b353af346d 100644 (file)
       <module fileurl="file://$PROJECT_DIR$/plugins/ant/intellij.ant.iml" filepath="$PROJECT_DIR$/plugins/ant/intellij.ant.iml" />
       <module fileurl="file://$PROJECT_DIR$/plugins/ant/jps-plugin/intellij.ant.jps.iml" filepath="$PROJECT_DIR$/plugins/ant/jps-plugin/intellij.ant.jps.iml" />
       <module fileurl="file://$PROJECT_DIR$/plugins/commander/intellij.commander.iml" filepath="$PROJECT_DIR$/plugins/commander/intellij.commander.iml" />
+      <module fileurl="file://$PROJECT_DIR$/plugins/configuration-script/intellij.configurationScript.iml" filepath="$PROJECT_DIR$/plugins/configuration-script/intellij.configurationScript.iml" />
       <module fileurl="file://$PROJECT_DIR$/plugins/copyright/intellij.copyright.iml" filepath="$PROJECT_DIR$/plugins/copyright/intellij.copyright.iml" />
       <module fileurl="file://$PROJECT_DIR$/plugins/cucumber-jvm-formatter/intellij.cucumber.jvmFormatter.iml" filepath="$PROJECT_DIR$/plugins/cucumber-jvm-formatter/intellij.cucumber.jvmFormatter.iml" />
       <module fileurl="file://$PROJECT_DIR$/plugins/cucumber-jvm-formatter3/intellij.cucumber.jvmFormatter3.iml" filepath="$PROJECT_DIR$/plugins/cucumber-jvm-formatter3/intellij.cucumber.jvmFormatter3.iml" />
index 44dbe4409c94ee3b26eda64004af8cdcb573ede5..aa56ebd6cdd04a181f7e116476b710b7c773e890 100644 (file)
@@ -1,6 +1,4 @@
-/*
- * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
- */
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package com.intellij.execution.applet;
 
 import com.intellij.execution.*;
@@ -46,11 +44,6 @@ public class AppletConfiguration extends ModuleBasedConfiguration<JavaRunConfigu
   }
 
   @Override
-  protected Class<AppletConfigurationOptions> getOptionsClass() {
-    return AppletConfigurationOptions.class;
-  }
-
-  @Override
   public void setMainClass(final PsiClass psiClass) {
     final Module originalModule = getConfigurationModule().getModule();
     setMainClassName(JavaExecutionUtil.getRuntimeQualifiedName(psiClass));
index 5c412f94b79df795e33f8ba581b27cf6f792f9cb..7b5aa16afe13fce86775968b3578f038a38aeead 100644 (file)
@@ -5,6 +5,7 @@ import com.intellij.execution.ExecutionBundle;
 import com.intellij.execution.configuration.ConfigurationFactoryEx;
 import com.intellij.execution.configurations.*;
 import com.intellij.icons.AllIcons;
+import com.intellij.openapi.components.BaseState;
 import com.intellij.openapi.project.Project;
 import org.jetbrains.annotations.NotNull;
 
@@ -25,6 +26,11 @@ public class AppletConfigurationType implements ConfigurationType {
       public void onNewConfigurationCreated(@NotNull RunConfiguration configuration) {
         ((ModuleBasedConfiguration)configuration).onNewConfigurationCreated();
       }
+
+      @Override
+      public Class<? extends BaseState> getOptionsClass() {
+        return AppletConfigurationOptions.class;
+      }
     };
   }
 
index bf50695f9a0c430ddde753ba9b59e6df81692c35..ed7f5634c5d1b0b91bd592032877cc1298723637 100644 (file)
@@ -46,15 +46,15 @@ public class ApplicationConfiguration extends ModuleBasedConfiguration<JavaRunCo
   private ShortenCommandLine myShortenCommandLine = null;
   private final InputRedirectAware.InputRedirectOptions myInputRedirectOptions = new InputRedirectOptions();
 
-  public ApplicationConfiguration(final String name, final Project project, ApplicationConfigurationType applicationConfigurationType) {
-    this(name, project, applicationConfigurationType.getConfigurationFactories()[0]);
+  public ApplicationConfiguration(String name, @NotNull Project project, @NotNull ApplicationConfigurationType configurationType) {
+    this(name, project, configurationType.getConfigurationFactories()[0]);
   }
 
-  public ApplicationConfiguration(final String name, final Project project) {
+  public ApplicationConfiguration(final String name, @NotNull Project project) {
     this(name, project, ApplicationConfigurationType.getInstance().getConfigurationFactories()[0]);
   }
 
-  protected ApplicationConfiguration(final String name, final Project project, final ConfigurationFactory factory) {
+  protected ApplicationConfiguration(String name, @NotNull Project project, @NotNull ConfigurationFactory factory) {
     super(name, new JavaRunConfigurationModule(project, true), factory);
   }
 
@@ -67,11 +67,6 @@ public class ApplicationConfiguration extends ModuleBasedConfiguration<JavaRunCo
   }
 
   @Override
-  protected Class<? extends ModuleBasedConfigurationOptions> getOptionsClass() {
-    return ApplicationConfigurationOptions.class;
-  }
-
-  @Override
   public void setMainClass(@NotNull PsiClass psiClass) {
     final Module originalModule = getConfigurationModule().getModule();
     setMainClassName(JavaExecutionUtil.getRuntimeQualifiedName(psiClass));
index 4b4c9a2b638be2afbf1dfbd92a7d5ec2a7012f37..db3d2b88853401a11053d15ce8f4732697f59f51 100644 (file)
@@ -23,5 +23,5 @@ open class ApplicationConfigurationOptions : JvmConfigurationOptions() {
   var isPassParentEnv: Boolean by property(true)
 
   @get:XMap(propertyElementName = "envs", entryTagName = "env", keyAttributeName = "name")
-  var env: MutableMap<String, String> by property(LinkedHashMap<String, String>())
+  var env: MutableMap<String, String> by property(LinkedHashMap())
 }
\ No newline at end of file
index 8d4b634b77c429b2f5330a2cb3a500392f807d7a..e10c17cf280708186ba5ace59e503dc6a50bf009 100644 (file)
@@ -1,24 +1,11 @@
-/*
- * Copyright 2000-2016 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package com.intellij.execution.application;
 
 import com.intellij.execution.ExecutionBundle;
 import com.intellij.execution.configuration.ConfigurationFactoryEx;
 import com.intellij.execution.configurations.*;
 import com.intellij.icons.AllIcons;
+import com.intellij.openapi.components.BaseState;
 import com.intellij.openapi.project.Project;
 import com.intellij.psi.PsiClass;
 import com.intellij.psi.PsiElement;
@@ -44,6 +31,11 @@ public class ApplicationConfigurationType implements ConfigurationType {
       public void onNewConfigurationCreated(@NotNull RunConfiguration configuration) {
         ((ModuleBasedConfiguration)configuration).onNewConfigurationCreated();
       }
+
+      @Override
+      public Class<? extends BaseState> getOptionsClass() {
+        return ApplicationConfigurationOptions.class;
+      }
     };
   }
 
@@ -103,6 +95,13 @@ public class ApplicationConfigurationType implements ConfigurationType {
   }
 
   @NotNull
+  @Override
+  public String getConfigurationPropertyName() {
+    String id = getId();
+    return id.equals("Application") ? "jvmApplication" : id;
+  }
+
+  @NotNull
   public static ApplicationConfigurationType getInstance() {
     return ConfigurationTypeUtil.findConfigurationType(ApplicationConfigurationType.class);
   }
index c14ccdd98603fd65032fd8774589fbb74f4061a0..eeaf7a2d433d5e1c620b00b99ac21740b7b18a99 100644 (file)
@@ -65,6 +65,12 @@ public class RemoteConfigurationType implements ConfigurationType {
   }
 
   @NotNull
+  @Override
+  public String getConfigurationPropertyName() {
+    return "jvmRemote";
+  }
+
+  @NotNull
   public static RemoteConfigurationType getInstance() {
     return ConfigurationTypeUtil.findConfigurationType(RemoteConfigurationType.class);
   }
index a280c1dd11b960d7f53036b62a807156114c42c1..d757568e7371e0a64057e92b959599ccab130ebb 100644 (file)
@@ -1,6 +1,4 @@
-/*
- * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
- */
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package com.intellij.execution.scratch;
 
 import com.intellij.debugger.DebuggerManager;
@@ -32,8 +30,7 @@ import java.io.File;
  * @author Eugene Zhuravlev
  */
 public class JavaScratchConfiguration extends ApplicationConfiguration {
-
-  protected JavaScratchConfiguration(String name, Project project, ConfigurationFactory factory) {
+  protected JavaScratchConfiguration(String name, Project project, @NotNull ConfigurationFactory factory) {
     super(name, project, factory);
   }
 
@@ -116,9 +113,4 @@ public class JavaScratchConfiguration extends ApplicationConfiguration {
   protected JavaScratchConfigurationOptions getOptions() {
     return (JavaScratchConfigurationOptions)super.getOptions();
   }
-
-  @Override
-  protected Class<? extends ModuleBasedConfigurationOptions> getOptionsClass() {
-    return JavaScratchConfigurationOptions.class;
-  }
 }
index 6b6b740008473c66d65eeafd166001fbe675e634..eafbad7b32be9c9382f86ecec2dc6759f2ce0445 100644 (file)
@@ -8,6 +8,7 @@ import com.intellij.execution.configurations.ConfigurationTypeUtil;
 import com.intellij.execution.configurations.ModuleBasedConfiguration;
 import com.intellij.execution.configurations.RunConfiguration;
 import com.intellij.icons.AllIcons;
+import com.intellij.openapi.components.BaseState;
 import com.intellij.openapi.project.Project;
 import com.intellij.ui.LayeredIcon;
 import org.jetbrains.annotations.NotNull;
@@ -37,6 +38,11 @@ public class JavaScratchConfigurationType extends ApplicationConfigurationType{
       public void onNewConfigurationCreated(@NotNull RunConfiguration configuration) {
         ((ModuleBasedConfiguration)configuration).onNewConfigurationCreated();
       }
+
+      @Override
+      public Class<? extends BaseState> getOptionsClass() {
+        return JavaScratchConfigurationOptions.class;
+      }
     };
   }
 
index e36a9703ba02103527468c13de58412c1a4aa3ba..d84164f1db918ba6fe8091a359aa0e8268156166 100644 (file)
@@ -3,10 +3,12 @@ package com.intellij.execution.configurations;
 
 import com.intellij.execution.BeforeRunTask;
 import com.intellij.execution.RunManager;
+import com.intellij.openapi.components.BaseState;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.Key;
 import com.intellij.util.IconUtil;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
 
@@ -124,4 +126,9 @@ public abstract class ConfigurationFactory {
   public RunConfigurationSingletonPolicy getSingletonPolicy() {
     return RunConfigurationSingletonPolicy.SINGLE_INSTANCE;
   }
+
+  @Nullable
+  public Class<? extends BaseState> getOptionsClass() {
+    return null;
+  }
 }
index e986e11ac7ac347aa87babb9fd9ff063ae3666b4..bd32c3380c4df1d489e2f1f502b081e6622e35da 100644 (file)
@@ -15,7 +15,6 @@ import javax.swing.*;
  * @see ConfigurationTypeBase
  */
 public interface ConfigurationType extends PossiblyDumbAware {
-
   ExtensionPointName<ConfigurationType> CONFIGURATION_TYPE_EP = ExtensionPointName.create("com.intellij.configurationType");
 
   /**
@@ -51,6 +50,14 @@ public interface ConfigurationType extends PossiblyDumbAware {
   String getId();
 
   /**
+   * The name of the configuration type in a configuration file. The same rules as for id. Useful in cases, where id cannot be changed.
+   */
+  @NotNull
+  default String getConfigurationPropertyName() {
+    return getId();
+  }
+
+  /**
    * Returns the configuration factories used by this configuration type. Normally each configuration type provides just a single factory.
    * You can return multiple factories if your configurations can be created in multiple variants (for example, local and remote for an
    * application server).
index bf40a3ab8699edf869074e7c7bcac10c34fcec36..bfbf65e0603b81225128e313df500b7f0dbfcc73 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package com.intellij.execution.configurations;
 
 import com.intellij.openapi.project.Project;
@@ -23,8 +23,9 @@ public abstract class LocatableConfigurationBase extends RunConfigurationBase im
     return (LocatableRunConfigurationOptions)super.getOptions();
   }
 
+  @NotNull
   @Override
-  protected Class<? extends LocatableRunConfigurationOptions> getOptionsClass() {
+  protected Class<? extends LocatableRunConfigurationOptions> getDefaultOptionsClass() {
     return LocatableRunConfigurationOptions.class;
   }
 
index 70a644865049b97ce9ec6645552dd28773a8dad1..415fed52fca4e403e2ace92c4ce93daf48f52100 100644 (file)
@@ -1,6 +1,4 @@
-/*
- * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
- */
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package com.intellij.execution.configurations;
 
 import com.intellij.openapi.application.ReadAction;
@@ -57,8 +55,9 @@ public abstract class ModuleBasedConfiguration<ConfigurationModule extends RunCo
     return (ModuleBasedConfigurationOptions)super.getOptions();
   }
 
+  @NotNull
   @Override
-  protected Class<? extends ModuleBasedConfigurationOptions> getOptionsClass() {
+  protected Class<? extends ModuleBasedConfigurationOptions> getDefaultOptionsClass() {
     return ModuleBasedConfigurationOptions.class;
   }
 
index cb5f2dafe9431492aa58bdbff309671e1634d1bc..6201f195ec5036ead122c261f22f4741b62c5f4a 100644 (file)
@@ -8,6 +8,7 @@ import com.intellij.execution.BeforeRunTask;
 import com.intellij.execution.ExecutionTarget;
 import com.intellij.execution.process.ProcessHandler;
 import com.intellij.execution.runners.ProgramRunner;
+import com.intellij.openapi.components.BaseState;
 import com.intellij.openapi.components.PersistentStateComponent;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.InvalidDataException;
@@ -40,7 +41,7 @@ public abstract class RunConfigurationBase extends UserDataHolderBase implements
   private final Project myProject;
   private String myName;
 
-  private RunConfigurationOptions myOptions = createOptions();
+  private RunConfigurationOptions myOptions;
 
   private List<PredefinedLogFile> myPredefinedLogFiles = new SmartList<>();
 
@@ -50,6 +51,8 @@ public abstract class RunConfigurationBase extends UserDataHolderBase implements
     myProject = project;
     myFactory = factory;
     myName = name;
+    // must be after factory because factory is used to get options class
+    myOptions = createOptions();
   }
 
   @NotNull
@@ -208,11 +211,30 @@ public abstract class RunConfigurationBase extends UserDataHolderBase implements
     myOptions = XmlSerializer.deserialize(element, getOptionsClass());
   }
 
+  // we can break compatibility and make this method final (API is new and used only by our plugins), but let's avoid any inconvenience and mark as "final" after/prior to 2018.3 release.
+  /**
+   * Do not override this method, use {@link ConfigurationFactory#getOptionsClass()}.
+   */
   protected Class<? extends RunConfigurationOptions> getOptionsClass() {
-    if (this instanceof PersistentStateComponent) {
+    Class<? extends BaseState> result = myFactory == null ? null : myFactory.getOptionsClass();
+    if (result != null) {
+      //noinspection unchecked
+      return (Class<? extends RunConfigurationOptions>)result;
+    }
+    else if (this instanceof PersistentStateComponent) {
       PersistentStateComponent instance = (PersistentStateComponent)this;
       return ComponentSerializationUtil.getStateClass(instance.getClass());
     }
+    else {
+      return getDefaultOptionsClass();
+    }
+  }
+
+  /**
+   * Do not override this method, it is intended to support old (not migrated to options class) run configurations.
+   */
+  @NotNull
+  protected Class<? extends RunConfigurationOptions> getDefaultOptionsClass() {
     return RunConfigurationOptions.class;
   }
 
index 4d51d54cbbabc7a4bb397fe05111db83fcb3bc17..8fc5087803b09a775c688ab1075ba2e7b6c44c9b 100644 (file)
@@ -1,6 +1,4 @@
-/*
- * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
- */
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package com.intellij.configurationStore.properties
 
 import com.intellij.openapi.components.BaseState
@@ -13,6 +11,9 @@ import kotlin.reflect.KProperty
  * AbstractCollectionBinding modifies collection directly, so, we cannot use null as default null and return empty list on get.
  */
 internal open class CollectionStoredProperty<E, C : MutableCollection<E>>(protected val value: C) : StoredPropertyBase<C>() {
+  override val jsonType: String
+    get() = "array"
+
   override fun isEqualToDefault() = value.isEmpty()
 
   override operator fun getValue(thisRef: BaseState, property: KProperty<*>) = value
@@ -50,6 +51,9 @@ internal class ListStoredProperty<T> : CollectionStoredProperty<T, SmartList<T>>
 }
 
 internal class MapStoredProperty<K: Any, V>(private val value: MutableMap<K, V>) : StoredPropertyBase<MutableMap<K, V>>() {
+  override val jsonType: String
+    get() = "object"
+
   override fun isEqualToDefault() = value.isEmpty()
 
   override operator fun getValue(thisRef: BaseState, property: KProperty<*>) = value
index a170a2e83c2a8a7bc8f001030743ce80db73c669..d97924501664e4fa29d6f13b03dc6cd88e1463c6 100644 (file)
@@ -1,6 +1,4 @@
-/*
- * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
- */
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package com.intellij.configurationStore.properties
 
 import com.intellij.openapi.components.BaseState
@@ -11,6 +9,9 @@ import kotlin.reflect.KProperty
 internal class FloatStoredProperty(private val defaultValue: Float, private val valueNormalizer: ((value: Float) -> Float)?) : StoredPropertyBase<Float>() {
   private var value = defaultValue
 
+  override val jsonType: String
+    get() = "number"
+
   override operator fun getValue(thisRef: BaseState, property: KProperty<*>) = value
 
   override fun setValue(thisRef: BaseState, property: KProperty<*>, value: Float) {
index 47ba90ae3adae0bb246268329f5738aa76db45c8..e09ee872ea15c8f5cdd3be1b234e498aeb10ed98 100644 (file)
@@ -1,6 +1,4 @@
-/*
- * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
- */
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package com.intellij.configurationStore.properties
 
 import com.intellij.openapi.components.BaseState
@@ -11,6 +9,9 @@ import kotlin.reflect.KProperty
 internal class IntStoredProperty(private val defaultValue: Int, private val valueNormalizer: ((value: Int) -> Int)?) : StoredPropertyBase<Int>() {
   private var value = defaultValue
 
+  override val jsonType: String
+    get() = "integer"
+
   override operator fun getValue(thisRef: BaseState, property: KProperty<*>) = value
 
   override fun setValue(thisRef: BaseState, property: KProperty<*>, value: Int) {
index 9c635d9566db4fe529b91ce9c1cb7d16ee8a27d2..6a44ef643220196f91a38b03c9fbfccffd8fc757 100644 (file)
@@ -1,6 +1,4 @@
-/*
- * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
- */
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package com.intellij.configurationStore.properties
 
 import com.intellij.openapi.components.BaseState
@@ -11,6 +9,9 @@ import kotlin.reflect.KProperty
 internal class LongStoredProperty(private val defaultValue: Long, private val valueNormalizer: ((value: Long) -> Long)?) : StoredPropertyBase<Long>() {
   private var value = defaultValue
 
+  override val jsonType: String
+    get() = "integer"
+
   override operator fun getValue(thisRef: BaseState, property: KProperty<*>) = value
 
   override fun setValue(thisRef: BaseState, property: KProperty<*>, value: Long) {
index 0f8a4a8ea29967aa436925dce715f18790470613..abf925c68c0ae61dfd737513951d5f6344077a67 100644 (file)
@@ -1,6 +1,4 @@
-/*
- * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
- */
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package com.intellij.configurationStore.properties
 
 import com.intellij.openapi.components.BaseState
@@ -10,6 +8,9 @@ import com.intellij.openapi.util.ModificationTracker
 import kotlin.reflect.KProperty
 
 internal abstract class ObjectStateStoredPropertyBase<T>(protected var value: T) : StoredPropertyBase<T>() {
+  override val jsonType: String
+    get() = "object"
+
   override operator fun getValue(thisRef: BaseState, property: KProperty<*>): T = value
 
   override fun setValue(thisRef: BaseState, property: KProperty<*>, @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") newValue: T) {
@@ -39,6 +40,9 @@ internal abstract class ObjectStateStoredPropertyBase<T>(protected var value: T)
 }
 
 internal open class ObjectStoredProperty<T>(private val defaultValue: T) : ObjectStateStoredPropertyBase<T>(defaultValue) {
+  override val jsonType: String
+    get() = if (defaultValue is Boolean) "boolean" else "object"
+
   override fun isEqualToDefault(): Boolean {
     val value = value
     return defaultValue == value || (value as? BaseState)?.isEqualToDefault() ?: false
index b94798acb2049e756216bf15175f9fe733a5b98b..405eeb2fe86fa3a7a327febf6f4260cc3c432c69 100644 (file)
@@ -1,6 +1,4 @@
-/*
- * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
- */
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package com.intellij.configurationStore.properties
 
 import com.intellij.openapi.components.BaseState
@@ -11,6 +9,9 @@ import kotlin.reflect.KProperty
 internal class NormalizedStringStoredProperty(private val defaultValue: String?) : StoredPropertyBase<String?>() {
   private var value = defaultValue
 
+  override val jsonType: String
+    get() = "string"
+
   override operator fun getValue(thisRef: BaseState, property: KProperty<*>) = value
 
   override fun setValue(thisRef: BaseState, property: KProperty<*>, value: String?) {
index 47aa268970925518031f23bbcd36cf554d36b4f9..d06faa846e72d469d17ef9f12f01b5e4aca5ae7c 100644 (file)
@@ -10,6 +10,7 @@ import com.intellij.util.xmlb.PropertyAccessor
 import com.intellij.util.xmlb.SerializationFilter
 import com.intellij.util.xmlb.annotations.Transient
 import gnu.trove.THashMap
+import org.jetbrains.annotations.ApiStatus
 import java.nio.charset.Charset
 import java.util.concurrent.atomic.AtomicLongFieldUpdater
 
@@ -169,6 +170,20 @@ abstract class BaseState : SerializationFilter, ModificationTracker {
 
   fun isEqualToDefault(): Boolean = properties.all { it.isEqualToDefault() }
 
+  // internal usage only
+  @ApiStatus.Experimental
+  fun buildJsonSchema(builder: StringBuilder) {
+    // todo object definition
+    for (property in properties) {
+      builder.jsonEscapedString(property.name!!).append(':').append('{')
+      builder.jsonEscapedString("type").append(':').jsonEscapedString(property.jsonType)
+      builder.append('}')
+      if (property !== properties.last()) {
+        builder.append(',')
+      }
+    }
+  }
+
   @Transient
   override fun getModificationCount(): Long {
     var result = ownModificationCount
@@ -211,3 +226,8 @@ abstract class BaseState : SerializationFilter, ModificationTracker {
     }
   }
 }
+
+private fun StringBuilder.jsonEscapedString(value: String): StringBuilder {
+  append('"').append(value).append('"')
+  return this
+}
\ No newline at end of file
index 3070fb54fed0eb7394acc83677d07b16d3d1844e..0b59407ed70f9de557f9562cac4751dc62bfeca1 100644 (file)
@@ -1,6 +1,4 @@
-/*
- * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
- */
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package com.intellij.openapi.components
 
 import kotlin.properties.ReadWriteProperty
@@ -9,6 +7,8 @@ import kotlin.reflect.KProperty
 internal interface StoredProperty {
   var name: String?
 
+  val jsonType: String
+
   // true if changed
   fun setValue(other: StoredProperty): Boolean
 
diff --git a/plugins/configuration-script/intellij.configurationScript.iml b/plugins/configuration-script/intellij.configurationScript.iml
new file mode 100644 (file)
index 0000000..5e08981
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" packagePrefix="com.intellij.configurationScript" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="KotlinJavaRuntime" level="project" />
+    <orderEntry type="module" module-name="intellij.platform.util" />
+    <orderEntry type="module" module-name="intellij.json" />
+    <orderEntry type="module" module-name="intellij.platform.lang" />
+    <orderEntry type="module" module-name="intellij.platform.testFramework" scope="TEST" />
+    <orderEntry type="module" module-name="intellij.platform.testExtensions" scope="TEST" />
+    <orderEntry type="module" module-name="intellij.platform.ide.impl" />
+  </component>
+</module>
\ No newline at end of file
diff --git a/plugins/configuration-script/resources/META-INF/plugin.xml b/plugins/configuration-script/resources/META-INF/plugin.xml
new file mode 100644 (file)
index 0000000..0dc365c
--- /dev/null
@@ -0,0 +1,14 @@
+<idea-plugin>
+  <name>IntelliJ Configuration Script</name>
+  <id>com.intellij.configurationScript</id>
+  <vendor>JetBrains</vendor>
+
+  <!--<depends>org.jetbrains.kotlin</depends>-->
+
+  <!--<extensions defaultExtensionNs="org.jetbrains.kotlin">-->
+  <!--<scriptDefinitionContributor implementation="com.intellij.configurationScript.ConfigurationScriptContributor" order="first"/>-->
+  <!--</extensions>-->
+  <extensions defaultExtensionNs="JavaScript.JsonSchema">
+    <ProviderFactory implementation="com.intellij.configurationScript.IdeConfigurationJsonSchemaProviderFactory"/>
+  </extensions>
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/configuration-script/src/com/intellij/configurationScript/IdeConfigurationJsonSchemaProviderFactory.kt b/plugins/configuration-script/src/com/intellij/configurationScript/IdeConfigurationJsonSchemaProviderFactory.kt
new file mode 100644 (file)
index 0000000..5145ad3
--- /dev/null
@@ -0,0 +1,88 @@
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package com.intellij.configurationScript
+
+import com.intellij.openapi.diagnostic.logger
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.util.text.StringUtil
+import com.intellij.openapi.vfs.VirtualFile
+import com.intellij.testFramework.LightVirtualFile
+import com.intellij.util.SystemProperties
+import com.jetbrains.jsonSchema.extension.JsonSchemaFileProvider
+import com.jetbrains.jsonSchema.extension.JsonSchemaProviderFactory
+import com.jetbrains.jsonSchema.extension.SchemaType
+import com.jetbrains.jsonSchema.impl.JsonSchemaVersion
+import org.intellij.lang.annotations.Language
+
+internal val LOG = logger<IdeConfigurationJsonSchemaProviderFactory>()
+
+internal class IdeConfigurationJsonSchemaProviderFactory : JsonSchemaProviderFactory, JsonSchemaFileProvider {
+  private val schemeFile: VirtualFile by lazy { generateConfigurationSchema() }
+
+  override fun getProviders(project: Project): List<JsonSchemaFileProvider> {
+    return listOf(this)
+  }
+
+  override fun isAvailable(file: VirtualFile): Boolean {
+    val nameSequence = file.nameSequence
+    return (nameSequence.endsWith(".yaml") || nameSequence.endsWith(".yml")) && StringUtil.equals(file.parent?.nameSequence, Project.DIRECTORY_STORE_FOLDER)
+  }
+
+  override fun getName() = "IntelliJ Configuration"
+
+  override fun getSchemaFile(): VirtualFile? {
+    if (SystemProperties.getBooleanProperty("configuration.schema.cache", true)) {
+      return schemeFile
+    }
+    else {
+      // simplify development - ability to apply changes on hotswap
+      return generateConfigurationSchema()
+    }
+  }
+
+  override fun getSchemaType() = SchemaType.embeddedSchema
+
+  override fun getSchemaVersion() = JsonSchemaVersion.SCHEMA_7
+}
+
+private fun generateConfigurationSchema(): LightVirtualFile {
+  // fake vars to avoid escaping
+  @Suppress("JsonStandardCompliance")
+  val schema = "\$schema"
+  @Suppress("JsonStandardCompliance")
+  val id = "\$id"
+  @Suppress("JsonStandardCompliance")
+  val ref = "\$ref"
+
+  val runConfigurationsProperties = StringBuilder()
+  val definitions = StringBuilder()
+  buildRunConfigurationTypeSchema(runConfigurationsProperties, definitions)
+
+  @Language("JSON")
+  val data = """
+  {
+    "$schema": "http://json-schema.org/draft-07/schema#",
+    "$id": "https://jetbrains.com/intellij-configuration.schema.json",
+    "title": "IntelliJ Configuration",
+    "description": "IntelliJ Configuration File to configure IDE behavior, run configurations and so on",
+    "type": "object",
+    "definitions": {
+      $definitions
+      "RunConfigurations": {
+        "properties": {
+          $runConfigurationsProperties
+        },
+        "additionalProperties": false
+      }
+    },
+    "properties": {
+      "runConfigurations": {
+        "description": "The run configurations",
+        "type": "object",
+        "$ref": "#/definitions/RunConfigurations"
+      }
+    },
+    "additionalProperties": false
+  }
+  """
+  return LightVirtualFile("scheme.json", data.trimIndent())
+}
\ No newline at end of file
diff --git a/plugins/configuration-script/src/com/intellij/configurationScript/runConfigurations.kt b/plugins/configuration-script/src/com/intellij/configurationScript/runConfigurations.kt
new file mode 100644 (file)
index 0000000..4c8ac01
--- /dev/null
@@ -0,0 +1,190 @@
+package com.intellij.configurationScript
+
+import com.intellij.execution.configurations.ConfigurationFactory
+import com.intellij.execution.configurations.ConfigurationType
+import com.intellij.openapi.util.text.StringUtil
+import com.intellij.util.ReflectionUtil
+import org.jetbrains.io.JsonUtil
+
+@Suppress("JsonStandardCompliance")
+private const val ref = "\$ref"
+
+internal fun buildRunConfigurationTypeSchema(properties: StringBuilder, definitions: StringBuilder) {
+  val configurationTypes = ConfigurationType.CONFIGURATION_TYPE_EP.extensionList
+  for (configurationType in configurationTypes) {
+    val propertyName = rcTypeIdToPropertyName(configurationType)
+    val definitionId = "${propertyName}RC"
+    val description = configurationType.configurationTypeDescription
+    val descriptionField: CharSequence = when {
+      StringUtil.equals(propertyName, description) -> ""
+      else -> {
+        val builder = StringBuilder()
+        builder.append('"').append("description").append('"').append(':')
+        JsonUtil.escape(description, builder)
+        builder.append(',')
+        builder
+      }
+    }
+    properties.append("""
+      "$propertyName": {
+        "type": [
+          "array",
+          "object"
+        ],
+        $descriptionField
+        "items": {
+          "$ref": "#/definitions/$definitionId"
+        },
+        "$ref": "#/definitions/$definitionId"
+      }
+      """.trimIndent())
+    if (configurationType !== configurationTypes.last()) {
+      properties.append(',')
+    }
+
+    describeFactories(configurationType, definitions, definitionId)
+  }
+}
+
+fun describeFactories(configurationType: ConfigurationType, definitions: StringBuilder, definitionId: String) {
+  val factories = configurationType.configurationFactories
+  if (factories.isEmpty()) {
+    LOG.error("Configuration type \"${configurationType.displayName}\" is not valid: factory list is empty")
+  }
+
+  val rcProperties = StringBuilder()
+  if (factories.size > 1) {
+    for (factory in factories) {
+      rcProperties.append("""
+          "${idToPropertyName(factory.id, null, factory)}": {
+            "type": "object"
+          },
+        """.trimIndent())
+      // todo describe factory object with properties
+    }
+    return
+  }
+
+  val factory = factories[0]
+  val optionsClass = factory.optionsClass
+  if (optionsClass == null) {
+    LOG.warn("Configuration factory \"${factory.name}\" is not described because options class not defined")
+
+    definitions.append("""
+      "$definitionId": {
+        "additionalProperties": true
+      },
+      """.trimIndent())
+    return
+  }
+
+  val state = ReflectionUtil.newInstance(optionsClass)
+  val stateProperties = StringBuilder()
+  state.buildJsonSchema(stateProperties)
+
+  definitions.append("""
+      "$definitionId": {
+        "properties": {
+          ${stateProperties}
+        },
+        "additionalProperties": false
+      },
+      """.trimIndent())
+}
+
+// returns null if id is not valid
+internal fun rcTypeIdToPropertyName(configurationType: ConfigurationType): CharSequence? {
+  return idToPropertyName(configurationType.configurationPropertyName, configurationType, null)
+}
+
+// returns null if id is not valid
+private fun idToPropertyName(string: String, configurationType: ConfigurationType?, factory: ConfigurationFactory?): CharSequence? {
+  val result = string
+    .removeSuffix("Type")
+    .removeSuffix("RunConfiguration")
+    .removeSuffix("Configuration")
+  if (result.isEmpty()) {
+    if (factory == null) {
+      LOG.error("Configuration type \"${configurationType!!.displayName}\" is not valid: id is empty")
+    }
+    else {
+      LOG.error("Configuration factory \"${factory.name}\" is not valid: id is empty")
+    }
+    return null
+  }
+
+  var builder: StringBuilder? = null
+  var i = 0
+  var isAllUpperCased = true
+  while (i < result.length) {
+    val ch = result[i]
+    @Suppress("IfThenToSafeAccess")
+    if (ch == '.' || ch == ' ' || ch == '-' || ch == '_') {
+      if (builder == null) {
+        builder = StringBuilder()
+        builder.append(result, 0, i)
+      }
+
+      i++
+      if (i == result.length) {
+        break
+      }
+      else {
+        builder.append(result[i].toUpperCase())
+        i++
+        continue
+      }
+    }
+    else if (ch == '#') {
+      i++
+      continue
+    }
+    else if (ch == '"' || ch == '\'' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\b' || ch == '/' || ch == '\\') {
+      if (factory == null) {
+        LOG.error("Configuration type \"${configurationType!!.id}\" is not valid: contains invalid symbol \"$ch\"")
+      }
+      else {
+        // todo for factory historically was no id (opposite to type), so, in most cases name can contain invalid chars, may be should be ignored instead of error?
+        LOG.error("Configuration factory \"${factory.name}\" is not valid: contains invalid symbol \"$ch\"")
+      }
+      return null
+    }
+    else if (i == 0) {
+      if (ch.isUpperCase()) {
+        if (builder == null) {
+          builder = StringBuilder()
+          builder.append(result, 0, i)
+        }
+        builder.append(ch.toLowerCase())
+      }
+      else {
+        isAllUpperCased = false
+      }
+    }
+    else if (builder != null) {
+      builder.append(ch)
+    }
+
+    if (!ch.isUpperCase()) {
+      isAllUpperCased = false
+    }
+
+    i++
+  }
+
+  if (isAllUpperCased) {
+    if (builder == null) {
+      return result.toLowerCase()
+    }
+    else {
+      @Suppress("NAME_SHADOWING")
+      for (i in 0 until builder.length) {
+        builder.setCharAt(i, builder.get(i).toLowerCase())
+      }
+      return builder
+    }
+  }
+  else {
+    return builder ?: result
+  }
+}
\ No newline at end of file
diff --git a/plugins/configuration-script/test/ConfigurationFileTest.kt b/plugins/configuration-script/test/ConfigurationFileTest.kt
new file mode 100644 (file)
index 0000000..ba213a3
--- /dev/null
@@ -0,0 +1,28 @@
+package com.intellij.configurationScript
+
+import com.intellij.execution.configurations.ConfigurationTypeBase
+import com.intellij.testFramework.assertions.Assertions.assertThat
+import org.junit.Test
+import javax.swing.Icon
+
+class ConfigurationFileTest {
+  @Test
+  fun rcId() {
+    fun convert(string: String): String {
+      return rcTypeIdToPropertyName(TestConfigurationType(string)).toString()
+    }
+
+    assertThat(convert("foo")).isEqualTo("foo")
+    assertThat(convert("Foo")).isEqualTo("foo")
+    assertThat(convert("foo-bar")).isEqualTo("fooBar")
+    assertThat(convert("foo.bar")).isEqualTo("fooBar")
+    assertThat(convert("foo_bar")).isEqualTo("fooBar")
+    assertThat(convert("FOO")).isEqualTo("foo")
+    assertThat(convert("_FOO")).isEqualTo("foo")
+    // better will be barfoo but for now we don't support this strange case
+    @Suppress("SpellCheckingInspection")
+    assertThat(convert("BAR_FOO")).isEqualTo("barfoo")
+  }
+}
+
+private class TestConfigurationType(id: String) : ConfigurationTypeBase(id, id, "", null as Icon?)
\ No newline at end of file
index 7d88662a20ee085c472473f1bb686fcf412ec30d..bccf5bf797755c13c0e9ef96bf680ab542d04cad 100644 (file)
@@ -85,6 +85,12 @@ public class PluginConfigurationType implements ConfigurationType {
   }
 
   @NotNull
+  @Override
+  public String getConfigurationPropertyName() {
+    return "plugin";
+  }
+
+  @NotNull
   private String getVmParameters() {
     if (myVmParameters == null) {
       String vmOptions;
index de60e04598c9950ebe89d44416e26341f25aaa4f..a07b61c595b5b2856e7a901461f703da7905d244 100644 (file)
@@ -1,18 +1,4 @@
-/*
- * Copyright 2000-2009 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 
 package com.intellij.execution.junit;
 
@@ -71,6 +57,13 @@ public class JUnitConfigurationType implements ConfigurationType {
     return "JUnit";
   }
 
+  @NotNull
+  @Override
+  public String getConfigurationPropertyName() {
+    String id = getId();
+    return id.equals("JUnit") ? "junit" : id;
+  }
+
   @Override
   public boolean isDumbAware() {
     return false;