From 1dbac8c385d28bad58eae727249229396bb429fb Mon Sep 17 00:00:00 2001 From: Rustam Vishnyakov Date: Tue, 20 Nov 2018 12:43:52 +0300 Subject: [PATCH] Export code style to Json (IDEA-149529) --- .../codeStyle/JavaCodeStyleSettingsTest.java | 110 ++++++------- .../AbstractCodeStylePropertyMapper.java | 141 +++++++++++++++++ .../properties/CodeStylePropertyAccessor.java | 6 +- .../properties/CodeStylePropertyMapper.java | 147 ------------------ .../GeneralCodeStylePropertyMapper.java | 97 ++++++++++++ .../LanguageCodeStylePropertyMapper.java | 83 ++++++++++ .../properties/PropertyAccessorFactory.java | 27 ++++ .../properties/PropertyNameUtil.java | 16 +- .../properties/WrappingAccessor.java | 7 +- .../LanguageCodeStyleSettingsProvider.java | 8 +- .../json/CodeStyleSchemeJsonExporter.java | 86 ++++++++++ .../src/META-INF/LangExtensions.xml | 4 + .../testData/codeStyle/json/exportToJson.json | 13 ++ .../JsonInterchangeTest.java | 27 ++++ 14 files changed, 562 insertions(+), 210 deletions(-) create mode 100644 platform/lang-api/src/com/intellij/application/options/codeStyle/properties/AbstractCodeStylePropertyMapper.java delete mode 100644 platform/lang-api/src/com/intellij/application/options/codeStyle/properties/CodeStylePropertyMapper.java create mode 100644 platform/lang-api/src/com/intellij/application/options/codeStyle/properties/GeneralCodeStylePropertyMapper.java create mode 100644 platform/lang-api/src/com/intellij/application/options/codeStyle/properties/LanguageCodeStylePropertyMapper.java create mode 100644 platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/json/CodeStyleSchemeJsonExporter.java create mode 100644 platform/platform-tests/testData/codeStyle/json/exportToJson.json create mode 100644 platform/platform-tests/testSrc/com/intellij/ide/codeStyleSettings/JsonInterchangeTest.java diff --git a/java/java-tests/testSrc/com/intellij/java/psi/codeStyle/JavaCodeStyleSettingsTest.java b/java/java-tests/testSrc/com/intellij/java/psi/codeStyle/JavaCodeStyleSettingsTest.java index 6b89a6b1c704..0d1278334a03 100644 --- a/java/java-tests/testSrc/com/intellij/java/psi/codeStyle/JavaCodeStyleSettingsTest.java +++ b/java/java-tests/testSrc/com/intellij/java/psi/codeStyle/JavaCodeStyleSettingsTest.java @@ -15,7 +15,7 @@ */ package com.intellij.java.psi.codeStyle; -import com.intellij.application.options.codeStyle.properties.CodeStylePropertyMapper; +import com.intellij.application.options.codeStyle.properties.AbstractCodeStylePropertyMapper; import com.intellij.ide.codeStyleSettings.CodeStyleTestCase; import com.intellij.lang.java.JavaLanguage; import com.intellij.openapi.application.ex.PathManagerEx; @@ -75,7 +75,7 @@ public class JavaCodeStyleSettingsTest extends CodeStyleTestCase { final JavaCodeStyleSettings javaSettings = settings.getCustomSettings(JavaCodeStyleSettings.class); javaSettings.FIELD_NAME_PREFIX = "m_"; javaSettings.STATIC_FIELD_NAME_SUFFIX = "_s"; - CodeStylePropertyMapper mapper = + AbstractCodeStylePropertyMapper mapper = LanguageCodeStyleSettingsProvider.forLanguage(JavaLanguage.INSTANCE).getPropertyMapper(settings); StringBuilder builder = new StringBuilder(); for (String property : mapper.enumPropertiesFor( @@ -108,15 +108,15 @@ public class JavaCodeStyleSettingsTest extends CodeStyleTestCase { "align_multiline_throws_list = false\n" + "align_subsequent_simple_methods = false\n" + "align_throws_keyword = false\n" + - "annotation_parameter_wrap = never\n" + + "annotation_parameter_wrap = no_wrap\n" + "array_initializer_left_brace_on_next_line = false\n" + "array_initializer_right_brace_on_next_line = false\n" + - "array_initializer_wrap = never\n" + + "array_initializer_wrap = no_wrap\n" + "assert_statement_colon_on_next_line = false\n" + - "assert_statement_wrap = never\n" + - "assignment_wrap = never\n" + + "assert_statement_wrap = no_wrap\n" + + "assignment_wrap = no_wrap\n" + "binary_operation_sign_on_next_line = false\n" + - "binary_operation_wrap = never\n" + + "binary_operation_wrap = no_wrap\n" + "blank_lines_after_anonymous_class_header = 0\n" + "blank_lines_after_class_header = 0\n" + "blank_lines_after_imports = 1\n" + @@ -138,7 +138,7 @@ public class JavaCodeStyleSettingsTest extends CodeStyleTestCase { "call_parameters_wrap = on_every_item\n" + "case_statement_on_new_line = true\n" + "catch_on_new_line = false\n" + - "class_annotation_wrap = always\n" + + "class_annotation_wrap = split_into_lines\n" + "class_brace_style = end_of_line\n" + "class_count_to_use_import_on_demand = 5\n" + "class_names_in_javadoc = 1\n" + @@ -148,20 +148,21 @@ public class JavaCodeStyleSettingsTest extends CodeStyleTestCase { "do_while_brace_force = never\n" + "else_on_new_line = false\n" + "enable_javadoc_formatting = true\n" + - "enum_constants_wrap = never\n" + - "extends_keyword_wrap = never\n" + - "extends_list_wrap = never\n" + - "field_annotation_wrap = always\n" + + "enum_constants_wrap = no_wrap\n" + + "extends_keyword_wrap = no_wrap\n" + + "extends_list_wrap = no_wrap\n" + + "field_annotation_wrap = split_into_lines\n" + "finally_on_new_line = false\n" + "for_brace_force = never\n" + "for_statement_left_paren_on_next_line = false\n" + "for_statement_right_paren_on_next_line = false\n" + - "for_statement_wrap = never\n" + + "for_statement_wrap = no_wrap\n" + "generate_final_locals = false\n" + "generate_final_parameters = false\n" + "if_brace_force = never\n" + "indent_case_from_switch = true\n" + "indent_size = 4\n" + + "indent_style = space\n" + "insert_inner_class_imports = false\n" + "insert_override_annotation = true\n" + "javadoc_add_blank_after_description = true\n" + @@ -198,15 +199,15 @@ public class JavaCodeStyleSettingsTest extends CodeStyleTestCase { "layout_static_imports_separately = true\n" + "line_comment_add_space = false\n" + "line_comment_at_first_column = true\n" + - "method_annotation_wrap = always\n" + + "method_annotation_wrap = split_into_lines\n" + "method_brace_style = end_of_line\n" + - "method_call_chain_wrap = never\n" + + "method_call_chain_wrap = no_wrap\n" + "method_parameters_left_paren_on_next_line = false\n" + "method_parameters_right_paren_on_next_line = false\n" + - "method_parameters_wrap = if_long\n" + + "method_parameters_wrap = normal\n" + "modifier_list_wrap = false\n" + "names_count_to_use_import_on_demand = 3\n" + - "parameter_annotation_wrap = never\n" + + "parameter_annotation_wrap = no_wrap\n" + "parentheses_expression_left_paren_wrap = false\n" + "parentheses_expression_right_paren_wrap = false\n" + "place_assignment_sign_on_next_line = false\n" + @@ -217,7 +218,7 @@ public class JavaCodeStyleSettingsTest extends CodeStyleTestCase { "replace_null_check = true\n" + "resource_list_left_paren_on_next_line = false\n" + "resource_list_right_paren_on_next_line = false\n" + - "resource_list_wrap = never\n" + + "resource_list_wrap = no_wrap\n" + "smart_tabs = false\n" + "space_after_closing_angle_bracket_in_type_argument = false\n" + "space_after_colon = true\n" + @@ -226,18 +227,6 @@ public class JavaCodeStyleSettingsTest extends CodeStyleTestCase { "space_after_for_semicolon = true\n" + "space_after_quest = true\n" + "space_after_type_cast = true\n" + - "space_around_additive_operators = true\n" + - "space_around_assignment_operators = true\n" + - "space_around_bitwise_operators = true\n" + - "space_around_equality_operators = true\n" + - "space_around_lambda_arrow = true\n" + - "space_around_logical_operators = true\n" + - "space_around_method_ref_dbl_colon = false\n" + - "space_around_multiplicative_operators = true\n" + - "space_around_relational_operators = true\n" + - "space_around_shift_operators = true\n" + - "space_around_type_bounds_in_type_parameters = true\n" + - "space_around_unary_operator = false\n" + "space_before_annotation_array_initializer_left_brace = false\n" + "space_before_anotation_parameter_list = false\n" + "space_before_array_initializer_left_brace = false\n" + @@ -274,38 +263,49 @@ public class JavaCodeStyleSettingsTest extends CodeStyleTestCase { "space_before_while_left_brace = true\n" + "space_before_while_parentheses = true\n" + "space_inside_one_line_enum_braces = false\n" + - "space_within_annotation_parentheses = false\n" + - "space_within_array_initializer_braces = false\n" + - "space_within_braces = false\n" + - "space_within_brackets = false\n" + - "space_within_cast_parentheses = false\n" + - "space_within_catch_parentheses = false\n" + - "space_within_empty_array_initializer_braces = false\n" + - "space_within_empty_method_call_parentheses = false\n" + - "space_within_empty_method_parentheses = false\n" + - "space_within_for_parentheses = false\n" + - "space_within_if_parentheses = false\n" + - "space_within_method_call_parentheses = false\n" + - "space_within_method_parentheses = false\n" + - "space_within_parentheses = false\n" + - "space_within_switch_parentheses = false\n" + - "space_within_synchronized_parentheses = false\n" + - "space_within_try_parentheses = false\n" + - "space_within_while_parentheses = false\n" + + "spaces_around_additive_operators = true\n" + + "spaces_around_assignment_operators = true\n" + + "spaces_around_bitwise_operators = true\n" + + "spaces_around_equality_operators = true\n" + + "spaces_around_lambda_arrow = true\n" + + "spaces_around_logical_operators = true\n" + + "spaces_around_method_ref_dbl_colon = false\n" + + "spaces_around_multiplicative_operators = true\n" + + "spaces_around_relational_operators = true\n" + + "spaces_around_shift_operators = true\n" + + "spaces_around_type_bounds_in_type_parameters = true\n" + + "spaces_around_unary_operator = false\n" + "spaces_within_angle_brackets = false\n" + + "spaces_within_annotation_parentheses = false\n" + + "spaces_within_array_initializer_braces = false\n" + + "spaces_within_braces = false\n" + + "spaces_within_brackets = false\n" + + "spaces_within_cast_parentheses = false\n" + + "spaces_within_catch_parentheses = false\n" + + "spaces_within_empty_array_initializer_braces = false\n" + + "spaces_within_empty_method_call_parentheses = false\n" + + "spaces_within_empty_method_parentheses = false\n" + + "spaces_within_for_parentheses = false\n" + + "spaces_within_if_parentheses = false\n" + + "spaces_within_method_call_parentheses = false\n" + + "spaces_within_method_parentheses = false\n" + + "spaces_within_parentheses = false\n" + + "spaces_within_switch_parentheses = false\n" + + "spaces_within_synchronized_parentheses = false\n" + + "spaces_within_try_parentheses = false\n" + + "spaces_within_while_parentheses = false\n" + "special_else_if_treatment = true\n" + "subclass_name_suffix = Impl\n" + - "tab_size = 4\n" + + "tab_width = 4\n" + "ternary_operation_signs_on_next_line = false\n" + - "ternary_operation_wrap = never\n" + + "ternary_operation_wrap = no_wrap\n" + "test_name_suffix = Test\n" + - "throws_keyword_wrap = never\n" + - "throws_list_wrap = never\n" + + "throws_keyword_wrap = no_wrap\n" + + "throws_list_wrap = no_wrap\n" + "use_external_annotations = false\n" + "use_fq_class_names = false\n" + "use_single_class_imports = true\n" + - "use_tab_character = false\n" + - "variable_annotation_wrap = never\n" + + "variable_annotation_wrap = no_wrap\n" + "visibility = public\n" + "while_brace_force = never\n" + "while_on_new_line = false\n" + @@ -318,7 +318,7 @@ public class JavaCodeStyleSettingsTest extends CodeStyleTestCase { public void testSetProperties() { final CodeStyleSettings settings = getCurrentCodeStyleSettings(); - CodeStylePropertyMapper mapper = + AbstractCodeStylePropertyMapper mapper = LanguageCodeStyleSettingsProvider.forLanguage(JavaLanguage.INSTANCE).getPropertyMapper(settings); mapper.setProperty("align_group_field_declarations", "true"); mapper.setProperty("blank_lines_after_class_header", "1"); diff --git a/platform/lang-api/src/com/intellij/application/options/codeStyle/properties/AbstractCodeStylePropertyMapper.java b/platform/lang-api/src/com/intellij/application/options/codeStyle/properties/AbstractCodeStylePropertyMapper.java new file mode 100644 index 000000000000..e7d3bf0b0321 --- /dev/null +++ b/platform/lang-api/src/com/intellij/application/options/codeStyle/properties/AbstractCodeStylePropertyMapper.java @@ -0,0 +1,141 @@ +// 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.application.options.codeStyle.properties; + +import com.intellij.openapi.util.AtomicNotNullLazyValue; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +public abstract class AbstractCodeStylePropertyMapper { + private @NotNull final CodeStyleSettings myRootSettings; + private final AtomicNotNullLazyValue> myAccessorMap; + + public AbstractCodeStylePropertyMapper(@NotNull CodeStyleSettings settings) { + myRootSettings = settings; + myAccessorMap = AtomicNotNullLazyValue.createValue(() -> createMap()); + } + + public void setProperty(@NotNull String name, @NotNull String value) { + if (getAccessorMap().containsKey(name)) { + myAccessorMap.getValue().get(name).set(value); + } + } + + @Nullable + public String getProperty(@NotNull String name) { + if (getAccessorMap().containsKey(name)) { + return getAccessorMap().get(name).get(); + } + return null; + } + + public List enumProperties() { + return getAccessorMap().keySet().stream().sorted().collect(Collectors.toList()); + } + + public List enumPropertiesFor(@NotNull Class... codeStyleClass) { + final Map accessorMap = myAccessorMap.getValue(); + return accessorMap.keySet().stream().filter(name -> { + CodeStylePropertyAccessor accessor = accessorMap.get(name); + for (Class aClass : codeStyleClass) { + if (accessor.getObjectClass().equals(aClass)) { + return true; + } + } + return false; + }).sorted().collect(Collectors.toList()); + } + + private Map createMap() { + Map accessorMap = ContainerUtil.newHashMap(); + for (CodeStyleObjectDescriptor descriptor : getSupportedFields()) { + addAccessorsFor(accessorMap, descriptor.getCodeStyleObject(), descriptor.getSupportedFields()); + } + return accessorMap; + } + + @NotNull + protected abstract List getSupportedFields(); + + private void addAccessorsFor(@NotNull Map accessorMap, + @NotNull Object codeStyleObject, + @Nullable Set supportedFields) { + Class codeStyleClass = codeStyleObject.getClass(); + for (Field field : getCodeStyleFields(codeStyleClass)) { + String fieldName = field.getName(); + if (supportedFields == null || supportedFields.contains(fieldName)) { + final CodeStylePropertyAccessor accessor = getAccessor(codeStyleObject, field); + if (accessor != null) { + accessorMap.put(accessor.getPropertyName(), accessor); + } + } + } + } + + @Nullable + protected CodeStylePropertyAccessor getAccessor(@NotNull Object codeStyleObject, @NotNull Field field) { + return new PropertyAccessorFactory(field).createAccessor(codeStyleObject); + } + + private List getCodeStyleFields(Class codeStyleClass) { + List fields = new ArrayList<>(); + Field[] allFields = useDeclaredFields() ? codeStyleClass.getDeclaredFields() : codeStyleClass.getFields(); + for (Field field : allFields) { + if (isPublic(field) && !isFinal(field)) { + fields.add(field); + } + } + return fields; + } + + private static boolean isPublic(final Field field) { + return (field.getModifiers() & Modifier.PUBLIC) != 0; + } + + private static boolean isFinal(final Field field) { + return (field.getModifiers() & Modifier.FINAL) != 0; + } + + @NotNull + protected CodeStyleSettings getRootSettings() { + return myRootSettings; + } + + @NotNull + private Map getAccessorMap() { + return myAccessorMap.getValue(); + } + + protected static final class CodeStyleObjectDescriptor { + private final Object myObject; + private final Set mySupportedFields; + + CodeStyleObjectDescriptor(@NotNull Object codeStyleObject, @Nullable Set fields) { + myObject = codeStyleObject; + mySupportedFields = fields; + } + + @NotNull + private Object getCodeStyleObject() { + return myObject; + } + + @Nullable + private Set getSupportedFields() { + return mySupportedFields; + } + } + + protected boolean useDeclaredFields() { + return false; + } +} diff --git a/platform/lang-api/src/com/intellij/application/options/codeStyle/properties/CodeStylePropertyAccessor.java b/platform/lang-api/src/com/intellij/application/options/codeStyle/properties/CodeStylePropertyAccessor.java index 92a3b3c1f3a0..d030f9bd4d51 100644 --- a/platform/lang-api/src/com/intellij/application/options/codeStyle/properties/CodeStylePropertyAccessor.java +++ b/platform/lang-api/src/com/intellij/application/options/codeStyle/properties/CodeStylePropertyAccessor.java @@ -34,7 +34,7 @@ public abstract class CodeStylePropertyAccessor { try { //noinspection unchecked T value = (T)myField.get(myObject); - return !isEmpty(value) ? asString(value) : null; + return value != null && !isEmpty(value) ? asString(value) : null; } catch (IllegalAccessException e) { // Ignore and return null @@ -56,4 +56,8 @@ public abstract class CodeStylePropertyAccessor { @NotNull protected abstract String asString(@NotNull T value); + + public String getPropertyName() { + return PropertyNameUtil.getPropertyName(myField.getName()); + } } diff --git a/platform/lang-api/src/com/intellij/application/options/codeStyle/properties/CodeStylePropertyMapper.java b/platform/lang-api/src/com/intellij/application/options/codeStyle/properties/CodeStylePropertyMapper.java deleted file mode 100644 index ad355dfb060c..000000000000 --- a/platform/lang-api/src/com/intellij/application/options/codeStyle/properties/CodeStylePropertyMapper.java +++ /dev/null @@ -1,147 +0,0 @@ -// 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.application.options.codeStyle.properties; - -import com.intellij.application.options.IndentOptionsEditor; -import com.intellij.application.options.SmartIndentOptionsEditor; -import com.intellij.lang.Language; -import com.intellij.psi.codeStyle.*; -import com.intellij.util.containers.ContainerUtil; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.*; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -@ApiStatus.Experimental -public class CodeStylePropertyMapper { - private @NotNull final CodeStyleSettings myRootSettings; - private @NotNull final Language myLanguage; - private final Map myAccessorMap = ContainerUtil.newHashMap(); - - public CodeStylePropertyMapper(@NotNull CodeStyleSettings settings, @NotNull Language language) { - myRootSettings = settings; - myLanguage = language; - fillMap(); - } - - public void setProperty(@NotNull String name, @NotNull String value) { - if (myAccessorMap.containsKey(name)) { - myAccessorMap.get(name).set(value); - } - } - - @Nullable - public String getProperty(@NotNull String name) { - if (myAccessorMap.containsKey(name)) { - return myAccessorMap.get(name).get(); - } - return null; - } - - public List enumProperties() { - return myAccessorMap.keySet().stream().sorted().collect(Collectors.toList()); - } - - public List enumPropertiesFor(@NotNull Class... codeStyleClass) { - return myAccessorMap.keySet().stream().filter(name -> { - CodeStylePropertyAccessor accessor = myAccessorMap.get(name); - for (Class aClass : codeStyleClass) { - if (accessor.getObjectClass().equals(aClass)) { - return true; - } - } - return false; - }).sorted().collect(Collectors.toList()); - } - - private void fillMap() { - CommonCodeStyleSettings.IndentOptions indentOptions = myRootSettings.getCommonSettings(myLanguage).getIndentOptions(); - if (indentOptions != null) { - addAccessorsFor(indentOptions, getSupportedIndentOptions()); - } - addAccessorsFor(myRootSettings.getCommonSettings(myLanguage), getSupportedFields()); - for (CustomCodeStyleSettings customSettings : getCustomSettings()) { - addAccessorsFor(customSettings, null); - } - } - - private List getCustomSettings() { - CodeStyleSettings rootSettings = new CodeStyleSettings(); - List customSettingsList = new ArrayList<>(); - addCustomSettings(customSettingsList, rootSettings, CodeStyleSettingsProvider.EXTENSION_POINT_NAME.getExtensionList()); - addCustomSettings(customSettingsList, rootSettings, LanguageCodeStyleSettingsProvider.getSettingsPagesProviders()); - return customSettingsList; - } - - private void addCustomSettings(@NotNull List list, - @NotNull CodeStyleSettings rootSettings, - @NotNull List providerList) { - for (CodeStyleSettingsProvider provider : providerList) { - if (provider.getLanguage() == myLanguage) { - CustomCodeStyleSettings customSettings = provider.createCustomSettings(rootSettings); - if (customSettings != null) { - list.add(customSettings); - } - } - } - } - - private Set getSupportedIndentOptions() { - LanguageCodeStyleSettingsProvider provider = LanguageCodeStyleSettingsProvider.forLanguage(myLanguage); - if (provider == null) return Collections.emptySet(); - Set indentOptions = ContainerUtil.newHashSet(); - IndentOptionsEditor editor = provider.getIndentOptionsEditor(); - if (editor != null) { - indentOptions.add("TAB_SIZE"); - indentOptions.add("USE_TAB_CHARACTER"); - indentOptions.add("INDENT_SIZE"); - if (editor instanceof SmartIndentOptionsEditor) { - indentOptions.add("CONTINUATION_INDENT_SIZE"); - indentOptions.add("SMART_TABS"); - indentOptions.add("KEEP_INDENTS_ON_EMPTY_LINES"); - } - } - return indentOptions; - } - - private Set getSupportedFields() { - LanguageCodeStyleSettingsProvider provider = LanguageCodeStyleSettingsProvider.forLanguage(myLanguage); - return provider == null ? Collections.emptySet() : provider.getSupportedFields(); - } - - private void addAccessorsFor(@NotNull Object codeStyleObject, @Nullable Set supportedFields) { - Class codeStyleClass = codeStyleObject.getClass(); - for (Field field : getCodeStyleFields(codeStyleClass)) { - String fieldName = field.getName(); - if (supportedFields == null || supportedFields.contains(fieldName)) { - final CodeStylePropertyAccessor accessor = new PropertyAccessorFactory(field).createAccessor(codeStyleObject); - if (accessor != null) { - myAccessorMap.put(PropertyNameUtil.getPropertyName(fieldName), accessor); - } - } - } - } - - private static List getCodeStyleFields(Class codeStyleClass) { - List fields = new ArrayList<>(); - for (Field field : codeStyleClass.getFields()) { - if (isPublic(field) && !isFinal(field)) { - fields.add(field); - } - } - return fields; - } - - private static boolean isPublic(final Field field) { - return (field.getModifiers() & Modifier.PUBLIC) != 0; - } - - private static boolean isFinal(final Field field) { - return (field.getModifiers() & Modifier.FINAL) != 0; - } - -} diff --git a/platform/lang-api/src/com/intellij/application/options/codeStyle/properties/GeneralCodeStylePropertyMapper.java b/platform/lang-api/src/com/intellij/application/options/codeStyle/properties/GeneralCodeStylePropertyMapper.java new file mode 100644 index 000000000000..598a4a53963f --- /dev/null +++ b/platform/lang-api/src/com/intellij/application/options/codeStyle/properties/GeneralCodeStylePropertyMapper.java @@ -0,0 +1,97 @@ +// 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.application.options.codeStyle.properties; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +public class GeneralCodeStylePropertyMapper extends AbstractCodeStylePropertyMapper { + private static final Logger LOG = Logger.getInstance(AbstractCodeStylePropertyMapper.class); + + private final static Set GENERAL_FIELDS = ContainerUtil.newHashSet( + "LINE_SEPARATOR", + "RIGHT_MARGIN", + "WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN", + "FORMATTER_TAGS_ENABLED", + "FORMATTER_ON_TAG", + "FORMATTER_OFF_TAG", + "FORMATTER_TAGS_ACCEPT_REGEXP" + ); + + public GeneralCodeStylePropertyMapper(@NotNull CodeStyleSettings settings) { + super(settings); + } + + @NotNull + @Override + protected List getSupportedFields() { + return Collections.singletonList(new CodeStyleObjectDescriptor(getRootSettings(), GENERAL_FIELDS)); + } + + @Override + protected boolean useDeclaredFields() { + return true; + } + + @Nullable + @Override + protected CodeStylePropertyAccessor getAccessor(@NotNull Object codeStyleObject, @NotNull Field field) { + if (codeStyleObject instanceof CodeStyleSettings) { + if ("LINE_SEPARATOR".equals(field.getName())) { + return new LineSeparatorAccessor(codeStyleObject, field); + } + } + return super.getAccessor(codeStyleObject, field); + } + + + private static class LineSeparatorAccessor extends StringAccessor { + LineSeparatorAccessor(@NotNull Object object, @NotNull Field field) { + super(object, field); + } + + @Nullable + @Override + protected String parseString(@NotNull String str) { + if (str.equals("lf")) { + return "\n"; + } + else if (str.equals("cr")) { + return "\r"; + } + else if (str.equals("crlf")) { + return "\r\f"; + } + return null; + } + + @NotNull + @Override + protected String asString(@NotNull String value) { + if ("\n".equals(value)) { + return "lf"; + } + else if ("\r".equals(value)) { + return "cr"; + } + else if ("\r\n".equals(value)) { + return "crlf"; + } + LOG.error("Unexpected field value: " + value); + return ""; + } + + @Override + protected boolean isEmpty(@NotNull String value) { + return StringUtil.isEmpty(value); + } + } +} diff --git a/platform/lang-api/src/com/intellij/application/options/codeStyle/properties/LanguageCodeStylePropertyMapper.java b/platform/lang-api/src/com/intellij/application/options/codeStyle/properties/LanguageCodeStylePropertyMapper.java new file mode 100644 index 000000000000..d75430a6a941 --- /dev/null +++ b/platform/lang-api/src/com/intellij/application/options/codeStyle/properties/LanguageCodeStylePropertyMapper.java @@ -0,0 +1,83 @@ +// 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.application.options.codeStyle.properties; + +import com.intellij.application.options.IndentOptionsEditor; +import com.intellij.application.options.SmartIndentOptionsEditor; +import com.intellij.lang.Language; +import com.intellij.psi.codeStyle.*; +import com.intellij.psi.codeStyle.CommonCodeStyleSettings.IndentOptions; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +import java.util.*; + +@ApiStatus.Experimental +public class LanguageCodeStylePropertyMapper extends AbstractCodeStylePropertyMapper { + private @NotNull final Language myLanguage; + + public LanguageCodeStylePropertyMapper(@NotNull CodeStyleSettings settings, @NotNull Language language) { + super(settings); + myLanguage = language; + } + + @NotNull + @Override + protected List getSupportedFields() { + List fieldsDescriptors = ContainerUtil.newArrayList(); + IndentOptions indentOptions = getRootSettings().getCommonSettings(myLanguage).getIndentOptions(); + if (indentOptions != null) { + fieldsDescriptors.add(new CodeStyleObjectDescriptor(indentOptions, getSupportedIndentOptions())); + } + fieldsDescriptors.add(new CodeStyleObjectDescriptor(getRootSettings().getCommonSettings(myLanguage), getSupportedLanguageFields())); + for (CustomCodeStyleSettings customSettings : getCustomSettings()) { + fieldsDescriptors.add(new CodeStyleObjectDescriptor(customSettings, null)); + } + return fieldsDescriptors; + } + + private List getCustomSettings() { + CodeStyleSettings rootSettings = new CodeStyleSettings(); + List customSettingsList = new ArrayList<>(); + addCustomSettings(customSettingsList, rootSettings, CodeStyleSettingsProvider.EXTENSION_POINT_NAME.getExtensionList()); + addCustomSettings(customSettingsList, rootSettings, LanguageCodeStyleSettingsProvider.getSettingsPagesProviders()); + return customSettingsList; + } + + private void addCustomSettings(@NotNull List list, + @NotNull CodeStyleSettings rootSettings, + @NotNull List providerList) { + for (CodeStyleSettingsProvider provider : providerList) { + if (provider.getLanguage() == myLanguage) { + CustomCodeStyleSettings customSettings = provider.createCustomSettings(rootSettings); + if (customSettings != null) { + list.add(customSettings); + } + } + } + } + + private Set getSupportedIndentOptions() { + LanguageCodeStyleSettingsProvider provider = LanguageCodeStyleSettingsProvider.forLanguage(myLanguage); + if (provider == null) return Collections.emptySet(); + Set indentOptions = ContainerUtil.newHashSet(); + IndentOptionsEditor editor = provider.getIndentOptionsEditor(); + if (editor != null) { + indentOptions.add("TAB_SIZE"); + indentOptions.add("USE_TAB_CHARACTER"); + indentOptions.add("INDENT_SIZE"); + if (editor instanceof SmartIndentOptionsEditor) { + indentOptions.add("CONTINUATION_INDENT_SIZE"); + indentOptions.add("SMART_TABS"); + indentOptions.add("KEEP_INDENTS_ON_EMPTY_LINES"); + } + } + return indentOptions; + } + + private Set getSupportedLanguageFields() { + LanguageCodeStyleSettingsProvider provider = LanguageCodeStyleSettingsProvider.forLanguage(myLanguage); + return provider == null ? Collections.emptySet() : provider.getSupportedFields(); + } + +} diff --git a/platform/lang-api/src/com/intellij/application/options/codeStyle/properties/PropertyAccessorFactory.java b/platform/lang-api/src/com/intellij/application/options/codeStyle/properties/PropertyAccessorFactory.java index 0acba010192c..4cabc2719c94 100644 --- a/platform/lang-api/src/com/intellij/application/options/codeStyle/properties/PropertyAccessorFactory.java +++ b/platform/lang-api/src/com/intellij/application/options/codeStyle/properties/PropertyAccessorFactory.java @@ -60,6 +60,9 @@ class PropertyAccessorFactory { if (mayHaveAccessor()) { switch (getValueType()) { case BOOLEAN: + if ("USE_TAB_CHARACTER".equals(myField.getName())) { + return new TabCharPropertyAccessor(codeStyleObject, myField); + } return new BooleanAccessor(codeStyleObject, myField); case INT: return new IntegerAccessor(codeStyleObject, myField); @@ -84,4 +87,28 @@ class PropertyAccessorFactory { return myField.getType().getCanonicalName() != null && myField.getAnnotation(Deprecated.class) == null; } + + private static class TabCharPropertyAccessor extends BooleanAccessor { + + TabCharPropertyAccessor(@NotNull Object object, @NotNull Field field) { + super(object, field); + } + + @Nullable + @Override + protected Boolean parseString(@NotNull String str) { + return "tab".equalsIgnoreCase(str); + } + + @NotNull + @Override + protected String asString(@NotNull Boolean value) { + return value ? "tab" : "space"; + } + + @Override + public String getPropertyName() { + return "indent_style"; + } + } } diff --git a/platform/lang-api/src/com/intellij/application/options/codeStyle/properties/PropertyNameUtil.java b/platform/lang-api/src/com/intellij/application/options/codeStyle/properties/PropertyNameUtil.java index ef33dc6dd43b..aa3847dfdf6f 100644 --- a/platform/lang-api/src/com/intellij/application/options/codeStyle/properties/PropertyNameUtil.java +++ b/platform/lang-api/src/com/intellij/application/options/codeStyle/properties/PropertyNameUtil.java @@ -23,6 +23,9 @@ class PropertyNameUtil { addMapping("INSTANCEOF", "instance_of"); addMapping("DOWHILE", "do_while"); addMapping("PARM", "param"); + addMapping("RIGHT_MARGIN", "max_line_length"); + addMapping("TAB_SIZE", "tab_width"); + addMapping("WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN", "wrap_on_typing"); } private PropertyNameUtil() { @@ -35,7 +38,7 @@ class PropertyNameUtil { static String getPropertyName(@NotNull String fieldName) { if (FIELD_TO_PROPERTY_NAME_MAP.containsKey(fieldName)) return FIELD_TO_PROPERTY_NAME_MAP.get(fieldName); StringBuilder nameBuilder = new StringBuilder(); - String[] chunks = fieldName.split("_"); + String[] chunks = preprocess(fieldName).split("_"); for (String chunk : chunks) { if (nameBuilder.length() > 0) nameBuilder.append('_'); appendNamePart(nameBuilder, chunk); @@ -43,6 +46,17 @@ class PropertyNameUtil { return nameBuilder.toString(); } + @NotNull + private static String preprocess(@NotNull String fieldName) { + if (fieldName.startsWith("SPACE_WITHIN")) { + return fieldName.replace("SPACE_WITHIN", "SPACES_WITHIN"); + } + else if (fieldName.startsWith("SPACE_AROUND")) { + return fieldName.replace("SPACE_AROUND", "SPACES_AROUND"); + } + return fieldName; + } + private static void appendNamePart(@NotNull StringBuilder nameBuilder, @NotNull String chunk) { if (chunk.length() > 0) { if (FIELD_TO_PROPERTY_NAME_MAP.containsKey(chunk)) { diff --git a/platform/lang-api/src/com/intellij/application/options/codeStyle/properties/WrappingAccessor.java b/platform/lang-api/src/com/intellij/application/options/codeStyle/properties/WrappingAccessor.java index 47fb815af3da..7669c8d344ba 100644 --- a/platform/lang-api/src/com/intellij/application/options/codeStyle/properties/WrappingAccessor.java +++ b/platform/lang-api/src/com/intellij/application/options/codeStyle/properties/WrappingAccessor.java @@ -12,10 +12,11 @@ import java.util.List; class WrappingAccessor extends CodeStylePropertyAccessor { private final static BidirectionalMap WRAPPING_MAP = new BidirectionalMap<>(); static { - WRAPPING_MAP.put(CommonCodeStyleSettings.DO_NOT_WRAP, "never"); - WRAPPING_MAP.put(CommonCodeStyleSettings.WRAP_AS_NEEDED, "if_long"); + WRAPPING_MAP.put(CommonCodeStyleSettings.DO_NOT_WRAP, "no_wrap"); + WRAPPING_MAP.put(CommonCodeStyleSettings.WRAP_AS_NEEDED, "normal"); WRAPPING_MAP.put(CommonCodeStyleSettings.WRAP_ON_EVERY_ITEM, "on_every_item"); - WRAPPING_MAP.put(CommonCodeStyleSettings.WRAP_ALWAYS, "always"); + WRAPPING_MAP.put(CommonCodeStyleSettings.WRAP_AS_NEEDED | CommonCodeStyleSettings.WRAP_ON_EVERY_ITEM, "on_every_item"); + WRAPPING_MAP.put(CommonCodeStyleSettings.WRAP_ALWAYS, "split_into_lines"); } WrappingAccessor(@NotNull Object object, @NotNull Field field) { diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/LanguageCodeStyleSettingsProvider.java b/platform/lang-api/src/com/intellij/psi/codeStyle/LanguageCodeStyleSettingsProvider.java index fedaabcb2498..c52a380dfca2 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/LanguageCodeStyleSettingsProvider.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/LanguageCodeStyleSettingsProvider.java @@ -2,7 +2,8 @@ package com.intellij.psi.codeStyle; import com.intellij.application.options.CodeStyleBean; -import com.intellij.application.options.codeStyle.properties.CodeStylePropertyMapper; +import com.intellij.application.options.codeStyle.properties.AbstractCodeStylePropertyMapper; +import com.intellij.application.options.codeStyle.properties.LanguageCodeStylePropertyMapper; import com.intellij.application.options.IndentOptionsEditor; import com.intellij.lang.IdeLanguageCustomization; import com.intellij.lang.Language; @@ -365,7 +366,8 @@ public abstract class LanguageCodeStyleSettingsProvider extends CodeStyleSetting } @ApiStatus.Experimental - public CodeStylePropertyMapper getPropertyMapper(@NotNull CodeStyleSettings settings) { - return new CodeStylePropertyMapper(settings, getLanguage()); + @NotNull + public AbstractCodeStylePropertyMapper getPropertyMapper(@NotNull CodeStyleSettings settings) { + return new LanguageCodeStylePropertyMapper(settings, getLanguage()); } } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/json/CodeStyleSchemeJsonExporter.java b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/json/CodeStyleSchemeJsonExporter.java new file mode 100644 index 000000000000..f3283f391412 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/json/CodeStyleSchemeJsonExporter.java @@ -0,0 +1,86 @@ +// 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.psi.impl.source.codeStyle.json; + +import com.google.gson.*; +import com.intellij.application.options.codeStyle.properties.AbstractCodeStylePropertyMapper; +import com.intellij.application.options.codeStyle.properties.GeneralCodeStylePropertyMapper; +import com.intellij.openapi.options.SchemeExporter; +import com.intellij.psi.codeStyle.CodeStyleScheme; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider; +import com.intellij.util.ObjectUtils; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.TestOnly; + +import java.io.OutputStream; +import java.io.PrintWriter; +import java.lang.reflect.Type; +import java.util.*; + +public class CodeStyleSchemeJsonExporter extends SchemeExporter { + + public static final String CODE_STYLE_JSON_EXT = "codestyle.json"; + public static final String COMMON_OPTIONS = "All"; + + private static final Set myLanguagesToExport = ContainerUtil.newHashSet(); + + @Override + public void exportScheme(@NotNull CodeStyleScheme scheme, @NotNull OutputStream outputStream) { + GsonBuilder builder = new GsonBuilder(); + builder.setPrettyPrinting(); + builder.registerTypeHierarchyAdapter(AbstractCodeStylePropertyMapper.class, new JsonSerializer() { + + @Override + public JsonElement serialize(AbstractCodeStylePropertyMapper src, Type typeOfSrc, JsonSerializationContext context) { + JsonObject o = new JsonObject(); + for (String name : src.enumProperties()) { + String value = src.getProperty(name); + o.addProperty(name, value); + } + return o; + } + }); + Gson gson = builder.create(); + String json = gson.toJson(getOptions(scheme)); + try (PrintWriter writer = new PrintWriter(outputStream)) { + writer.write(json); + } + } + + @TestOnly + public CodeStyleSchemeJsonExporter filter(@NotNull String... languages) { + myLanguagesToExport.addAll(Arrays.asList(languages)); + return this; + } + + private static List getOptions(@NotNull CodeStyleScheme scheme) { + List options = ContainerUtil.newArrayList(); + final CodeStyleSettings codeStyleSettings = scheme.getCodeStyleSettings(); + for (LanguageCodeStyleSettingsProvider provider : LanguageCodeStyleSettingsProvider.EP_NAME.getExtensionList()) { + final String languageName = provider.getLanguage().getDisplayName(); + if (myLanguagesToExport.isEmpty() || myLanguagesToExport.contains(languageName)) { + options.add(new LanguageOptions(ObjectUtils.notNull(provider.getLanguageName(), languageName), + provider.getPropertyMapper(codeStyleSettings))); + } + } + Collections.sort(options, Comparator.comparing(o -> o.language)); + options.add(0, new LanguageOptions(COMMON_OPTIONS, new GeneralCodeStylePropertyMapper(codeStyleSettings))); + return options; + } + + @Override + public String getExtension() { + return CODE_STYLE_JSON_EXT; + } + + private static class LanguageOptions { + final @NotNull String language; + final @NotNull AbstractCodeStylePropertyMapper options; + + private LanguageOptions(@NotNull String language, @NotNull AbstractCodeStylePropertyMapper mapper) { + this.language = language; + this.options = mapper; + } + } +} diff --git a/platform/platform-resources/src/META-INF/LangExtensions.xml b/platform/platform-resources/src/META-INF/LangExtensions.xml index 69ebf97274c8..520a60b42014 100644 --- a/platform/platform-resources/src/META-INF/LangExtensions.xml +++ b/platform/platform-resources/src/META-INF/LangExtensions.xml @@ -1098,6 +1098,10 @@ name="Intellij IDEA color scheme (.icls) or settings (.jar)" schemeClass="com.intellij.openapi.editor.colors.EditorColorsScheme" implementationClass="com.intellij.application.options.colors.ColorSchemeImporter"/> +