Export code style to Json (IDEA-149529)

This commit is contained in:
Rustam Vishnyakov
2018-11-20 12:45:47 +03:00
parent e42d8ae688
commit 1dbac8c385
14 changed files with 562 additions and 210 deletions
@@ -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");
@@ -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<Map<String,CodeStylePropertyAccessor>> 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<String> enumProperties() {
return getAccessorMap().keySet().stream().sorted().collect(Collectors.toList());
}
public List<String> enumPropertiesFor(@NotNull Class... codeStyleClass) {
final Map<String, CodeStylePropertyAccessor> 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<String, CodeStylePropertyAccessor> createMap() {
Map<String, CodeStylePropertyAccessor> accessorMap = ContainerUtil.newHashMap();
for (CodeStyleObjectDescriptor descriptor : getSupportedFields()) {
addAccessorsFor(accessorMap, descriptor.getCodeStyleObject(), descriptor.getSupportedFields());
}
return accessorMap;
}
@NotNull
protected abstract List<CodeStyleObjectDescriptor> getSupportedFields();
private void addAccessorsFor(@NotNull Map<String, CodeStylePropertyAccessor> accessorMap,
@NotNull Object codeStyleObject,
@Nullable Set<String> 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<Field> getCodeStyleFields(Class codeStyleClass) {
List<Field> 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<String,CodeStylePropertyAccessor> getAccessorMap() {
return myAccessorMap.getValue();
}
protected static final class CodeStyleObjectDescriptor {
private final Object myObject;
private final Set<String> mySupportedFields;
CodeStyleObjectDescriptor(@NotNull Object codeStyleObject, @Nullable Set<String> fields) {
myObject = codeStyleObject;
mySupportedFields = fields;
}
@NotNull
private Object getCodeStyleObject() {
return myObject;
}
@Nullable
private Set<String> getSupportedFields() {
return mySupportedFields;
}
}
protected boolean useDeclaredFields() {
return false;
}
}
@@ -34,7 +34,7 @@ public abstract class CodeStylePropertyAccessor<T> {
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<T> {
@NotNull
protected abstract String asString(@NotNull T value);
public String getPropertyName() {
return PropertyNameUtil.getPropertyName(myField.getName());
}
}
@@ -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<String, CodeStylePropertyAccessor> 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<String> enumProperties() {
return myAccessorMap.keySet().stream().sorted().collect(Collectors.toList());
}
public List<String> 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<CustomCodeStyleSettings> getCustomSettings() {
CodeStyleSettings rootSettings = new CodeStyleSettings();
List<CustomCodeStyleSettings> customSettingsList = new ArrayList<>();
addCustomSettings(customSettingsList, rootSettings, CodeStyleSettingsProvider.EXTENSION_POINT_NAME.getExtensionList());
addCustomSettings(customSettingsList, rootSettings, LanguageCodeStyleSettingsProvider.getSettingsPagesProviders());
return customSettingsList;
}
private void addCustomSettings(@NotNull List<CustomCodeStyleSettings> list,
@NotNull CodeStyleSettings rootSettings,
@NotNull List<? extends CodeStyleSettingsProvider> providerList) {
for (CodeStyleSettingsProvider provider : providerList) {
if (provider.getLanguage() == myLanguage) {
CustomCodeStyleSettings customSettings = provider.createCustomSettings(rootSettings);
if (customSettings != null) {
list.add(customSettings);
}
}
}
}
private Set<String> getSupportedIndentOptions() {
LanguageCodeStyleSettingsProvider provider = LanguageCodeStyleSettingsProvider.forLanguage(myLanguage);
if (provider == null) return Collections.emptySet();
Set<String> 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<String> getSupportedFields() {
LanguageCodeStyleSettingsProvider provider = LanguageCodeStyleSettingsProvider.forLanguage(myLanguage);
return provider == null ? Collections.emptySet() : provider.getSupportedFields();
}
private void addAccessorsFor(@NotNull Object codeStyleObject, @Nullable Set<String> 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<Field> getCodeStyleFields(Class codeStyleClass) {
List<Field> 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;
}
}
@@ -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<String> 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<CodeStyleObjectDescriptor> 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);
}
}
}
@@ -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<CodeStyleObjectDescriptor> getSupportedFields() {
List<CodeStyleObjectDescriptor> 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<CustomCodeStyleSettings> getCustomSettings() {
CodeStyleSettings rootSettings = new CodeStyleSettings();
List<CustomCodeStyleSettings> customSettingsList = new ArrayList<>();
addCustomSettings(customSettingsList, rootSettings, CodeStyleSettingsProvider.EXTENSION_POINT_NAME.getExtensionList());
addCustomSettings(customSettingsList, rootSettings, LanguageCodeStyleSettingsProvider.getSettingsPagesProviders());
return customSettingsList;
}
private void addCustomSettings(@NotNull List<CustomCodeStyleSettings> list,
@NotNull CodeStyleSettings rootSettings,
@NotNull List<? extends CodeStyleSettingsProvider> providerList) {
for (CodeStyleSettingsProvider provider : providerList) {
if (provider.getLanguage() == myLanguage) {
CustomCodeStyleSettings customSettings = provider.createCustomSettings(rootSettings);
if (customSettings != null) {
list.add(customSettings);
}
}
}
}
private Set<String> getSupportedIndentOptions() {
LanguageCodeStyleSettingsProvider provider = LanguageCodeStyleSettingsProvider.forLanguage(myLanguage);
if (provider == null) return Collections.emptySet();
Set<String> 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<String> getSupportedLanguageFields() {
LanguageCodeStyleSettingsProvider provider = LanguageCodeStyleSettingsProvider.forLanguage(myLanguage);
return provider == null ? Collections.emptySet() : provider.getSupportedFields();
}
}
@@ -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";
}
}
}
@@ -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)) {
@@ -12,10 +12,11 @@ import java.util.List;
class WrappingAccessor extends CodeStylePropertyAccessor<Integer> {
private final static BidirectionalMap<Integer, String> 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) {
@@ -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());
}
}
@@ -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<CodeStyleScheme> {
public static final String CODE_STYLE_JSON_EXT = "codestyle.json";
public static final String COMMON_OPTIONS = "All";
private static final Set<String> 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<AbstractCodeStylePropertyMapper>() {
@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<LanguageOptions> getOptions(@NotNull CodeStyleScheme scheme) {
List<LanguageOptions> 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;
}
}
}
@@ -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"/>
<schemeExporter
name="JSON File"
schemeClass="com.intellij.psi.codeStyle.CodeStyleScheme"
implementationClass="com.intellij.psi.impl.source.codeStyle.json.CodeStyleSchemeJsonExporter"/>
<schemeExporter
name="Intellij IDEA inspection profile XML"
@@ -0,0 +1,13 @@
[
{
"language": "All",
"options": {
"formatter_off_tag": "@formatter:off",
"formatter_on_tag": "@formatter:on",
"formatter_tags_accept_regexp": "false",
"formatter_tags_enabled": "false",
"max_line_length": "120",
"wrap_on_typing": "false"
}
}
]
@@ -0,0 +1,27 @@
// 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.ide.codeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleScheme;
import com.intellij.psi.impl.source.codeStyle.json.CodeStyleSchemeJsonExporter;
import org.jetbrains.annotations.Nullable;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class JsonInterchangeTest extends CodeStyleTestCase {
public void testExportToJson() throws IOException {
CodeStyleScheme testScheme = createTestScheme();
CodeStyleSchemeJsonExporter exporter = new CodeStyleSchemeJsonExporter().filter("All");
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
exporter.exportScheme(testScheme, outputStream);
String expected = loadExpected("json");
assertEquals(expected, outputStream.toString());
}
@Nullable
@Override
protected String getTestDir() {
return "json";
}
}