Store selected set of defaults in code style settings (KT-22252)
#KT-22252 In Progress
This commit is contained in:
@@ -4,7 +4,7 @@ apply { plugin("kotlin") }
|
||||
dependencies {
|
||||
compile(project(":compiler:util"))
|
||||
compile(project(":compiler:frontend"))
|
||||
compileOnly(intellijDep()) { includeJars("idea", "openapi", "util") }
|
||||
compileOnly(intellijDep()) { includeJars("idea", "openapi", "util", "jdom") }
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
||||
+61
-3
@@ -18,9 +18,15 @@ package org.jetbrains.kotlin.idea.core.formatter;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.InvalidDataException;
|
||||
import com.intellij.openapi.util.WriteExternalException;
|
||||
import com.intellij.psi.codeStyle.*;
|
||||
import com.intellij.util.ReflectionUtil;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.idea.formatter.KotlinStyleGuideCodeStyle;
|
||||
import org.jetbrains.kotlin.idea.util.ReflectionUtil;
|
||||
|
||||
import static com.intellij.util.ReflectionUtil.copyFields;
|
||||
|
||||
public class KotlinCodeStyleSettings extends CustomCodeStyleSettings {
|
||||
public static final KotlinCodeStyleSettings DEFAULT = new KotlinCodeStyleSettings(new CodeStyleSettings());
|
||||
@@ -52,9 +58,20 @@ public class KotlinCodeStyleSettings extends CustomCodeStyleSettings {
|
||||
public int WRAP_ELVIS_EXPRESSIONS = 1;
|
||||
public boolean IF_RPAREN_ON_NEW_LINE = false;
|
||||
|
||||
@ReflectionUtil.SkipInEquals
|
||||
public String CODE_STYLE_DEFAULTS = null;
|
||||
|
||||
private final boolean isTempForDeserialize;
|
||||
|
||||
public KotlinCodeStyleSettings(CodeStyleSettings container) {
|
||||
this(container, false);
|
||||
}
|
||||
|
||||
private KotlinCodeStyleSettings(CodeStyleSettings container, boolean isTempForDeserialize) {
|
||||
super("JetCodeStyleSettings", container);
|
||||
|
||||
this.isTempForDeserialize = isTempForDeserialize;
|
||||
|
||||
// defaults in IDE but not in tests
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
PACKAGES_TO_USE_STAR_IMPORTS.addEntry(new PackageEntry(false, "java.util", false));
|
||||
@@ -74,8 +91,49 @@ public class KotlinCodeStyleSettings extends CustomCodeStyleSettings {
|
||||
}
|
||||
|
||||
private void copyFrom(@NotNull KotlinCodeStyleSettings from) {
|
||||
ReflectionUtil.copyFields(getClass().getFields(), from, this);
|
||||
|
||||
copyFields(getClass().getFields(), from, this);
|
||||
PACKAGES_TO_USE_STAR_IMPORTS.copyFrom(from.PACKAGES_TO_USE_STAR_IMPORTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof KotlinCodeStyleSettings)) return false;
|
||||
if (!ReflectionUtil.comparePublicNonFinalFieldsWithSkip(this, obj)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeExternal(Element parentElement, @NotNull CustomCodeStyleSettings parentSettings) throws WriteExternalException {
|
||||
if (KotlinStyleGuideCodeStyle.CODE_STYLE_ID.equals(CODE_STYLE_DEFAULTS)) {
|
||||
KotlinCodeStyleSettings defaultKotlinCodeStyle = (KotlinCodeStyleSettings) parentSettings.clone();
|
||||
KotlinStyleGuideCodeStyle.Companion.applyToKotlinCustomSettings(defaultKotlinCodeStyle, false);
|
||||
parentSettings = defaultKotlinCodeStyle;
|
||||
}
|
||||
|
||||
super.writeExternal(parentElement, parentSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readExternal(Element parentElement) throws InvalidDataException {
|
||||
if (isTempForDeserialize) {
|
||||
super.readExternal(parentElement);
|
||||
return;
|
||||
}
|
||||
|
||||
KotlinCodeStyleSettings tempSettings = readExternalToTemp(parentElement);
|
||||
if (KotlinStyleGuideCodeStyle.CODE_STYLE_ID.equals(tempSettings.CODE_STYLE_DEFAULTS)) {
|
||||
KotlinStyleGuideCodeStyle.Companion.applyToKotlinCustomSettings(this, true);
|
||||
}
|
||||
|
||||
// Actual read
|
||||
super.readExternal(parentElement);
|
||||
}
|
||||
|
||||
private static KotlinCodeStyleSettings readExternalToTemp(Element parentElement) {
|
||||
// Read to temp
|
||||
KotlinCodeStyleSettings tempSettings = new KotlinCodeStyleSettings(null, true);
|
||||
tempSettings.readExternal(parentElement);
|
||||
|
||||
return tempSettings;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,8 +44,8 @@ private val CODE_BLOCKS = TokenSet.create(KtNodeTypes.BLOCK, KtNodeTypes.CLASS_B
|
||||
private val ALIGN_FOR_BINARY_OPERATIONS = TokenSet.create(MUL, DIV, PERC, PLUS, MINUS, ELVIS, LT, GT, LTEQ, GTEQ, ANDAND, OROR)
|
||||
private val ANNOTATIONS = TokenSet.create(KtNodeTypes.ANNOTATION_ENTRY, KtNodeTypes.ANNOTATION)
|
||||
|
||||
val CodeStyleSettings.kotlinCommonSettings: CommonCodeStyleSettings
|
||||
get() = getCommonSettings(KotlinLanguage.INSTANCE)
|
||||
val CodeStyleSettings.kotlinCommonSettings: KotlinCommonCodeStyleSettings
|
||||
get() = getCommonSettings(KotlinLanguage.INSTANCE) as KotlinCommonCodeStyleSettings
|
||||
|
||||
val CodeStyleSettings.kotlinCustomSettings: KotlinCodeStyleSettings
|
||||
get() = getCustomSettings(KotlinCodeStyleSettings::class.java)!!
|
||||
|
||||
+238
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* 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/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.formatter;
|
||||
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.openapi.util.DefaultJDOMExternalizer;
|
||||
import com.intellij.openapi.util.DifferenceFilter;
|
||||
import com.intellij.openapi.util.InvalidDataException;
|
||||
import com.intellij.openapi.util.WriteExternalException;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
||||
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
|
||||
import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider;
|
||||
import com.intellij.psi.codeStyle.arrangement.ArrangementSettings;
|
||||
import com.intellij.psi.codeStyle.arrangement.ArrangementUtil;
|
||||
import com.intellij.util.xmlb.XmlSerializer;
|
||||
import kotlin.collections.ArraysKt;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.idea.KotlinLanguage;
|
||||
import org.jetbrains.kotlin.idea.util.ReflectionUtil;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@SuppressWarnings("UnnecessaryFinalOnLocalVariableOrParameter")
|
||||
public class KotlinCommonCodeStyleSettings extends CommonCodeStyleSettings {
|
||||
@ReflectionUtil.SkipInEquals
|
||||
public String CODE_STYLE_DEFAULTS = null;
|
||||
|
||||
private final boolean isTempForDeserialize;
|
||||
|
||||
public KotlinCommonCodeStyleSettings() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
private KotlinCommonCodeStyleSettings(boolean isTempForDeserialize) {
|
||||
super(KotlinLanguage.INSTANCE);
|
||||
this.isTempForDeserialize = isTempForDeserialize;
|
||||
}
|
||||
|
||||
private static KotlinCommonCodeStyleSettings createForTempDeserialize() {
|
||||
return new KotlinCommonCodeStyleSettings(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readExternal(Element element) throws InvalidDataException {
|
||||
if (isTempForDeserialize) {
|
||||
super.readExternal(element);
|
||||
return;
|
||||
}
|
||||
|
||||
KotlinCommonCodeStyleSettings tempDeserialize = createForTempDeserialize();
|
||||
tempDeserialize.readExternal(element);
|
||||
|
||||
if (KotlinStyleGuideCodeStyle.CODE_STYLE_ID.equals(tempDeserialize.CODE_STYLE_DEFAULTS)) {
|
||||
KotlinStyleGuideCodeStyle.Companion.applyToCommonSettings(this, true);
|
||||
}
|
||||
|
||||
readExternalBase(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeExternal(Element element) throws WriteExternalException {
|
||||
CommonCodeStyleSettings defaultSettings = getDefaultSettings();
|
||||
|
||||
if (defaultSettings != null && KotlinStyleGuideCodeStyle.CODE_STYLE_ID.equals(CODE_STYLE_DEFAULTS)) {
|
||||
KotlinStyleGuideCodeStyle.Companion.applyToCommonSettings(defaultSettings, false);
|
||||
}
|
||||
|
||||
writeExternalBase(element, defaultSettings);
|
||||
}
|
||||
|
||||
//<editor-fold desc="Copied and adapted from CommonCodeStyleSettings ">
|
||||
private void readExternalBase(Element element) throws InvalidDataException {
|
||||
super.readExternal(element);
|
||||
}
|
||||
|
||||
private void writeExternalBase(Element element, CommonCodeStyleSettings defaultSettings) throws WriteExternalException {
|
||||
Set<String> supportedFields = getSupportedFields();
|
||||
if (supportedFields != null) {
|
||||
supportedFields.add("FORCE_REARRANGE_MODE");
|
||||
supportedFields.add("CODE_STYLE_DEFAULTS");
|
||||
}
|
||||
//noinspection deprecation
|
||||
DefaultJDOMExternalizer.writeExternal(this, element, new SupportedFieldsDiffFilter(this, supportedFields, defaultSettings));
|
||||
List<Integer> softMargins = getSoftMargins();
|
||||
serializeInto(softMargins, element);
|
||||
|
||||
IndentOptions myIndentOptions = getIndentOptions();
|
||||
if (myIndentOptions != null) {
|
||||
IndentOptions defaultIndentOptions = defaultSettings != null ? defaultSettings.getIndentOptions() : null;
|
||||
Element indentOptionsElement = new Element(INDENT_OPTIONS_TAG);
|
||||
myIndentOptions.serialize(indentOptionsElement, defaultIndentOptions);
|
||||
if (!indentOptionsElement.getChildren().isEmpty()) {
|
||||
element.addContent(indentOptionsElement);
|
||||
}
|
||||
}
|
||||
|
||||
ArrangementSettings myArrangementSettings = getArrangementSettings();
|
||||
if (myArrangementSettings != null) {
|
||||
Element container = new Element(ARRANGEMENT_ELEMENT_NAME);
|
||||
ArrangementUtil.writeExternal(container, myArrangementSettings, myLanguage);
|
||||
if (!container.getChildren().isEmpty()) {
|
||||
element.addContent(container);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonCodeStyleSettings clone(@NotNull CodeStyleSettings rootSettings) {
|
||||
CommonCodeStyleSettings commonSettings = new KotlinCommonCodeStyleSettings();
|
||||
copyPublicFieldsOwn(this, commonSettings);
|
||||
|
||||
try {
|
||||
Method setRootSettingsMethod = CommonCodeStyleSettings.class.getDeclaredMethod("setRootSettings", CodeStyleSettings.class);
|
||||
setRootSettingsMethod.setAccessible(true);
|
||||
setRootSettingsMethod.invoke(commonSettings, rootSettings);
|
||||
}
|
||||
catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
|
||||
commonSettings.setForceArrangeMenuAvailable(isForceArrangeMenuAvailable());
|
||||
|
||||
IndentOptions indentOptions = getIndentOptions();
|
||||
if (indentOptions != null) {
|
||||
IndentOptions targetIndentOptions = commonSettings.initIndentOptions();
|
||||
targetIndentOptions.copyFrom(indentOptions);
|
||||
}
|
||||
|
||||
ArrangementSettings arrangementSettings = getArrangementSettings();
|
||||
if (arrangementSettings != null) {
|
||||
commonSettings.setArrangementSettings(arrangementSettings.clone());
|
||||
}
|
||||
|
||||
try {
|
||||
Method setRootSettingsMethod = ArraysKt.singleOrNull(
|
||||
CommonCodeStyleSettings.class.getDeclaredMethods(),
|
||||
method -> "setSoftMargins".equals(method.getName()));
|
||||
|
||||
if (setRootSettingsMethod != null) {
|
||||
// Method was introduced in 173
|
||||
setRootSettingsMethod.setAccessible(true);
|
||||
setRootSettingsMethod.invoke(commonSettings, getSoftMargins());
|
||||
}
|
||||
}
|
||||
catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
|
||||
return commonSettings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof KotlinCommonCodeStyleSettings)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ReflectionUtil.comparePublicNonFinalFieldsWithSkip(this, obj)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CommonCodeStyleSettings other = (CommonCodeStyleSettings) obj;
|
||||
if (!getSoftMargins().equals(other.getSoftMargins())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
IndentOptions options = getIndentOptions();
|
||||
if ((options == null && other.getIndentOptions() != null) ||
|
||||
(options != null && !options.equals(other.getIndentOptions()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return arrangementSettingsEqual(other);
|
||||
}
|
||||
|
||||
// SoftMargins.serializeInfo
|
||||
private void serializeInto(@NotNull List<Integer> softMargins, @NotNull Element element) {
|
||||
if (softMargins.size() > 0) {
|
||||
XmlSerializer.serializeInto(this, element);
|
||||
}
|
||||
}
|
||||
//</editor-fold>
|
||||
|
||||
//<editor-fold desc="Copied from CommonCodeStyleSettings">
|
||||
private static final String INDENT_OPTIONS_TAG = "indentOptions";
|
||||
private static final String ARRANGEMENT_ELEMENT_NAME = "arrangement";
|
||||
|
||||
private final Language myLanguage = KotlinLanguage.INSTANCE;
|
||||
|
||||
@Nullable
|
||||
private CommonCodeStyleSettings getDefaultSettings() {
|
||||
return LanguageCodeStyleSettingsProvider.getDefaultCommonSettings(myLanguage);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Set<String> getSupportedFields() {
|
||||
final LanguageCodeStyleSettingsProvider provider = LanguageCodeStyleSettingsProvider.forLanguage(myLanguage);
|
||||
return provider == null ? null : provider.getSupportedFields();
|
||||
}
|
||||
|
||||
private static class SupportedFieldsDiffFilter extends DifferenceFilter<CommonCodeStyleSettings> {
|
||||
private final Set<String> mySupportedFieldNames;
|
||||
|
||||
public SupportedFieldsDiffFilter(
|
||||
final CommonCodeStyleSettings object,
|
||||
Set<String> supportedFiledNames,
|
||||
final CommonCodeStyleSettings parentObject
|
||||
) {
|
||||
super(object, parentObject);
|
||||
mySupportedFieldNames = supportedFiledNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccept(@NotNull Field field) {
|
||||
if (mySupportedFieldNames == null ||
|
||||
mySupportedFieldNames.contains(field.getName())) {
|
||||
return super.isAccept(field);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Can't use super.copyPublicFields because the method is internal in 181
|
||||
private static void copyPublicFieldsOwn(Object from, Object to) {
|
||||
assert from != to;
|
||||
com.intellij.util.ReflectionUtil.copyFields(to.getClass().getFields(), from, to);
|
||||
}
|
||||
//</editor-fold>
|
||||
}
|
||||
+13
-2
@@ -28,13 +28,20 @@ class KotlinStyleGuideCodeStyle : PredefinedCodeStyle("Kotlin style guide", Kotl
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val CODE_STYLE_ID = "KOTLIN_OFFICIAL"
|
||||
const val CODE_STYLE_TITLE = "Kotlin Coding Conventions"
|
||||
|
||||
fun apply(settings: CodeStyleSettings) {
|
||||
applyToKotlinCustomSettings(settings.kotlinCustomSettings)
|
||||
applyToCommonSettings(settings.kotlinCommonSettings)
|
||||
}
|
||||
|
||||
fun applyToKotlinCustomSettings(kotlinCustomSettings: KotlinCodeStyleSettings) {
|
||||
fun applyToKotlinCustomSettings(kotlinCustomSettings: KotlinCodeStyleSettings, modifyCodeStyle: Boolean = true) {
|
||||
kotlinCustomSettings.apply {
|
||||
if (modifyCodeStyle) {
|
||||
CODE_STYLE_DEFAULTS = CODE_STYLE_ID
|
||||
}
|
||||
|
||||
CONTINUATION_INDENT_IN_PARAMETER_LISTS = false
|
||||
CONTINUATION_INDENT_IN_ARGUMENT_LISTS = false
|
||||
CONTINUATION_INDENT_FOR_EXPRESSION_BODIES = false
|
||||
@@ -46,7 +53,7 @@ class KotlinStyleGuideCodeStyle : PredefinedCodeStyle("Kotlin style guide", Kotl
|
||||
}
|
||||
}
|
||||
|
||||
fun applyToCommonSettings(commonSettings: CommonCodeStyleSettings) {
|
||||
fun applyToCommonSettings(commonSettings: CommonCodeStyleSettings, modifyCodeStyle: Boolean = true) {
|
||||
commonSettings.apply {
|
||||
WHILE_ON_NEW_LINE = false
|
||||
ELSE_ON_NEW_LINE = false
|
||||
@@ -67,6 +74,10 @@ class KotlinStyleGuideCodeStyle : PredefinedCodeStyle("Kotlin style guide", Kotl
|
||||
|
||||
ALIGN_MULTILINE_BINARY_OPERATION = false
|
||||
}
|
||||
|
||||
if (modifyCodeStyle && commonSettings is KotlinCommonCodeStyleSettings) {
|
||||
commonSettings.CODE_STYLE_DEFAULTS = CODE_STYLE_ID
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.util;
|
||||
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.Predicate;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Set;
|
||||
|
||||
public class ReflectionUtil {
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface SkipInEquals {}
|
||||
|
||||
public static boolean comparePublicNonFinalFieldsWithSkip(@NotNull Object first, @NotNull Object second) {
|
||||
return comparePublicNonFinalFields(first, second, field -> field.getAnnotation(SkipInEquals.class) == null);
|
||||
}
|
||||
|
||||
private static boolean comparePublicNonFinalFields(@NotNull Object first, @NotNull Object second, @Nullable Predicate<Field> acceptPredicate) {
|
||||
Set<Field> firstFields = ContainerUtil.newHashSet(first.getClass().getFields());
|
||||
|
||||
for (Field field : second.getClass().getFields()) {
|
||||
if (firstFields.contains(field)) {
|
||||
if (isPublic(field) && !isFinal(field) && (acceptPredicate == null || acceptPredicate.apply(field))) {
|
||||
try {
|
||||
if (!Comparing.equal(field.get(first), field.get(second))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isPublic(Field field) {
|
||||
return (field.getModifiers() & Modifier.PUBLIC) != 0;
|
||||
}
|
||||
|
||||
private static boolean isFinal(Field field) {
|
||||
return (field.getModifiers() & Modifier.FINAL) != 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user