diff --git a/java/java-impl/src/com/intellij/psi/codeStyle/extractor/differ/JavaExtractor.java b/java/java-impl/src/com/intellij/psi/codeStyle/extractor/differ/JavaExtractor.java new file mode 100644 index 000000000000..9032144354ca --- /dev/null +++ b/java/java-impl/src/com/intellij/psi/codeStyle/extractor/differ/JavaExtractor.java @@ -0,0 +1,69 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.codeStyle.extractor.differ; + +import com.intellij.lang.ASTNode; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.project.Project; +import com.intellij.psi.JavaCodeFragment; +import com.intellij.psi.JavaCodeFragmentFactory; +import com.intellij.psi.PsiFile; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.impl.source.SourceTreeToPsiMap; +import com.intellij.psi.impl.source.codeStyle.CodeFormatterFacade; +import com.intellij.psi.codeStyle.extractor.values.Value; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.LinkedList; + +/** + * @author Roman.Shein + * @since 05.08.2015. + * TODO: move from debugger-impl to more meaningful location? + * + * + */ +public class JavaExtractor implements LangCodeStyleExtractor { + @NotNull + @Override + public Differ getDiffer(final Project project, PsiFile psiFile, CodeStyleSettings settings) { + return new DifferBase(project, psiFile, settings) { + @Override + public String reformattedText() { + final JavaCodeFragment file = JavaCodeFragmentFactory.getInstance(project). + createCodeBlockCodeFragment(myOrigText, myFile, false); + + WriteCommandAction.runWriteCommandAction(myProject, "CodeStyleSettings extractor", "CodeStyleSettings extractor", new Runnable() { + @Override + public void run() { + final ASTNode treeElement = SourceTreeToPsiMap.psiElementToTree(file); + assert (treeElement != null); + + SourceTreeToPsiMap.treeElementToPsi(new CodeFormatterFacade(mySettings, file.getLanguage()).processElement(treeElement)); + } + }, file); + return file.getText(); + } + }; + } + + @NotNull + @Override + public Collection getCustomVarKinds() { + return new LinkedList(); + } +} diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/Utils.java b/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/Utils.java new file mode 100644 index 000000000000..dd7b3c922148 --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/Utils.java @@ -0,0 +1,196 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.codeStyle.extractor; + +import com.intellij.lang.Language; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.CodeStyleSettingsProvider; +import com.intellij.psi.codeStyle.CommonCodeStyleSettings; +import com.intellij.psi.codeStyle.CustomCodeStyleSettings; +import com.intellij.psi.codeStyle.extractor.differ.Differ; +import com.intellij.psi.codeStyle.extractor.values.Generation; +import com.intellij.psi.codeStyle.extractor.values.Gens; +import com.intellij.psi.codeStyle.extractor.values.Value; +import com.intellij.psi.codeStyle.extractor.values.ValuesExtractionResult; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class Utils { + public static int CRITICAL_SYMBOL_WEIGHT = 100; + + public static void logError(String message) { + System.out.println(message); //TODO reasonable implementation + } + + public static int getTabSize(@Nullable CommonCodeStyleSettings.IndentOptions options) { + return options == null ? 4 : options.TAB_SIZE; + } + + public static int getNormalizedLength(@Nullable CommonCodeStyleSettings.IndentOptions options, @NotNull String text) { + return text + .replaceAll( + "\t", + StringUtil.repeatSymbol(' ', getTabSize(options))) + .replaceAll( + "\n", + StringUtil.repeatSymbol(' ', CRITICAL_SYMBOL_WEIGHT)) + .length(); + } + + public static boolean isSpace(char c) { + return " \t\n".indexOf(c) >= 0; + } + + public static boolean isBracket(char c) { + return "(){}[]<>".indexOf(c) >= 0; + } + + public static int getDiff(@Nullable CommonCodeStyleSettings.IndentOptions options, @NotNull String oldV, @NotNull String newV) { + oldV = oldV.trim(); + newV = newV.trim(); + int diff = 0; + int oPos = 0, nPos = 0; + int oEnd = oldV.length(), nEnd = newV.length(); + + StringBuilder oSp = new StringBuilder(); + StringBuilder nSp = new StringBuilder(); + char ch; + while (oPos < oEnd || nPos < nEnd) { + oSp.setLength(0); + nSp.setLength(0); + while (oPos < oEnd && isSpace(ch = oldV.charAt(oPos))) { + oSp.append(ch); + ++oPos; + } + while (nPos < nEnd && isSpace(ch = newV.charAt(nPos))) { + nSp.append(ch); + ++nPos; + } + diff += Math.abs(getNormalizedLength(options, oSp.toString()) + - getNormalizedLength(options, nSp.toString())); //each time + + while (oPos < oEnd && nPos < nEnd + && (ch = oldV.charAt(oPos)) == newV.charAt(nPos) + && !isSpace(ch)) { + ++oPos; + ++nPos; + } + + if (oPos < oEnd && nPos < nEnd && !isSpace(oldV.charAt(oPos)) && !isSpace(newV.charAt(nPos))) { + logError("AST changed!"); + return Differ.UGLY_FORMATTING; + } + } + return diff; + } + + @Nullable + public static CustomCodeStyleSettings getLanguageSettings(@NotNull CodeStyleSettings settings, @NotNull Language language) { + for (CodeStyleSettingsProvider provider : CodeStyleSettingsProvider.EXTENSION_POINT_NAME.getExtensions()) { + if (language.equals(provider.getLanguage())) { + CustomCodeStyleSettings modelSettings = provider.createCustomSettings(settings); + if (modelSettings == null) continue; + CustomCodeStyleSettings customSettings = settings.getCustomSettings(modelSettings.getClass()); + if (customSettings != null) { + return customSettings; + } + } + } + logError("Failed to load CustomCodeStyleSettings for " + language); + return null; + } + + public static void adjustValuesMin( + @NotNull ValuesExtractionResult gens, + @NotNull Differ differ, + @Nullable ProgressIndicator indicator) { + + final List values = gens.getValues(); + final int length = values.size(); + int i = 0; + for (Value value : values) { + if (indicator != null) { + indicator.checkCanceled(); + indicator.setText2("Value:" + value.name); + indicator.setFraction(0.5 + 0.5 * i / length); + } + + Object bestValue = value.value; + int bestScope = differ.getDifference(gens); + for (Object cnst : value.getPossibleValues()) { + if (cnst.equals(value.value)) { + continue; + } + value.value = cnst; + int diff = differ.getDifference(gens); + if (diff < bestScope) { + bestValue = cnst; + bestScope = diff; + value.state = Value.STATE.SELECTED; + } + else if (diff > bestScope) { + value.state = Value.STATE.SELECTED; + } + } + value.value = bestValue; + ++i; + } + } + + public static void adjustValuesGA( + @NotNull Gens gens, + @NotNull Differ differ, + @Nullable ProgressIndicator indicator) { + + Generation generation = Generation.createZeroGeneration(gens); + while (generation.tryAgain()) { + if (indicator != null) { + indicator.checkCanceled(); + } + final int age = generation.getAge(); + if (indicator != null) { + indicator.setText2(" age:" + age + "/" + generation.getParentKind()); + indicator.setFraction(0.5 * age / Generation.GEN_COUNT); + } + generation = Generation.createNextGeneration(differ, generation); + } + gens.copyFrom(generation.getBestGens(differ)); + } + + static long rseed = 0; + static final long RAND_MAX_32 = ((1L << 31L) - 1L); + static final long RAND_MAX = ((1L << 15L) - 1L); + + public static void resetRandom() { + rseed = 0; + } + + public static long getRandom() { + rseed = (rseed * 214013 + 2531011) & RAND_MAX_32; + return rseed >> 16; + } + + public static int getRandomLess(int count) { + final int ret = (int)(getRandom() * count / RAND_MAX); + if (ret >= count) return count - 1; + if (ret < 0) return 0; + return ret; + } +} diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/differ/Differ.java b/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/differ/Differ.java new file mode 100644 index 000000000000..1da3a742385d --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/differ/Differ.java @@ -0,0 +1,24 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.codeStyle.extractor.differ; + +import com.intellij.psi.codeStyle.extractor.values.ValuesExtractionResult; + +public interface Differ { + int UGLY_FORMATTING = Integer.MAX_VALUE / 4; + + int getDifference(ValuesExtractionResult values); +} diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/differ/DifferBase.java b/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/differ/DifferBase.java new file mode 100644 index 000000000000..006d21ec974f --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/differ/DifferBase.java @@ -0,0 +1,51 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.codeStyle.extractor.differ; + +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiFile; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.extractor.Utils; +import com.intellij.psi.codeStyle.extractor.values.ValuesExtractionResult; + +/** + * @author Roman.Shein + * @since 30.07.2015. + */ +public abstract class DifferBase implements Differ { + protected final Project myProject; + protected final String myOrigText; + protected final CodeStyleSettings mySettings; + protected final PsiFile myFile; + + public DifferBase(Project project, PsiFile file, CodeStyleSettings settings) { + myProject = project; + myFile = file; + myOrigText = myFile.getText(); + mySettings = settings; + } + + public abstract String reformattedText(); + + @Override + public int getDifference(ValuesExtractionResult container) { + final ValuesExtractionResult orig = container.apply(true); + String newText = reformattedText(); + int result = Utils.getDiff(mySettings.getCommonSettings(myFile.getLanguage()).getIndentOptions(), myOrigText, newText); + orig.apply(false); + return result; + } +} diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/differ/LangCodeStyleExtractor.java b/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/differ/LangCodeStyleExtractor.java new file mode 100644 index 000000000000..388210b9a9ec --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/differ/LangCodeStyleExtractor.java @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.codeStyle.extractor.differ; + +import com.intellij.lang.LanguageExtension; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiFile; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.extractor.values.Value; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +/** + * @author Roman.Shein + * @since 31.07.2015. + */ +public interface LangCodeStyleExtractor { + LanguageExtension EXTENSION = + new LanguageExtension("com.intellij.lang.formatting.extractor"); + + @NotNull + Differ getDiffer(Project project, PsiFile psiFile, CodeStyleSettings settings); + + @NotNull + Collection getCustomVarKinds(); +} diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/processor/BruteForceProcessor.java b/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/processor/BruteForceProcessor.java new file mode 100644 index 000000000000..be14bc40bfbd --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/processor/BruteForceProcessor.java @@ -0,0 +1,62 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.codeStyle.extractor.processor; + +import com.intellij.lang.Language; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiFile; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.extractor.Utils; +import com.intellij.psi.codeStyle.extractor.differ.Differ; +import com.intellij.psi.codeStyle.extractor.differ.LangCodeStyleExtractor; +import com.intellij.psi.codeStyle.extractor.values.Value; +import com.intellij.psi.codeStyle.extractor.values.ValuesExtractionResult; +import com.intellij.psi.codeStyle.extractor.values.ValuesExtractionResultImpl; + +import java.util.List; +import java.util.Random; + +/** + * @author Roman.Shein + * @since 04.08.2015. + */ +public class BruteForceProcessor extends CodeStyleDeriveProcessor { + + public BruteForceProcessor(LangCodeStyleExtractor langExtractor) { + super(langExtractor); + } + + @Override + public ValuesExtractionResult runWithProgress(Project project, CodeStyleSettings settings, PsiFile file, ProgressIndicator indicator) { + List values = getFormattingValues(settings, file.getLanguage()); + Differ differ = myLangExtractor.getDiffer(project, file, settings); + ValuesExtractionResult container = new ValuesExtractionResultImpl(values); + Utils.adjustValuesMin(container, differ, indicator); + return container; + } + + public void randomizeSettings(CodeStyleSettings settings, Language language) { + List values = getFormattingValues(settings, language); + Random rand = new Random(); + for (Value value : values) { + Object[] possible = value.getPossibleValues(); + int index = rand.nextInt(possible.length); + value.value = possible[index]; + value.write(false); + } + } +} diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/processor/CodeStyleDeriveProcessor.java b/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/processor/CodeStyleDeriveProcessor.java new file mode 100644 index 000000000000..f99023b1c6a2 --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/processor/CodeStyleDeriveProcessor.java @@ -0,0 +1,143 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.codeStyle.extractor.processor; + +import com.intellij.lang.Language; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiFile; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.CommonCodeStyleSettings; +import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider; +import com.intellij.psi.codeStyle.extractor.Utils; +import com.intellij.psi.codeStyle.extractor.differ.LangCodeStyleExtractor; +import com.intellij.psi.codeStyle.extractor.values.ClassSerializer; +import com.intellij.psi.codeStyle.extractor.values.Value; +import com.intellij.psi.codeStyle.extractor.values.ValuesExtractionResult; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.Field; +import java.util.*; + +/** + * @author Roman.Shein + * @since 04.08.2015. + */ +public abstract class CodeStyleDeriveProcessor { + + protected final LangCodeStyleExtractor myLangExtractor; + + protected CodeStyleDeriveProcessor(LangCodeStyleExtractor langExtractor) { + myLangExtractor = langExtractor; + } + + public abstract ValuesExtractionResult runWithProgress(Project project, CodeStyleSettings settings, PsiFile file, + ProgressIndicator indicator); + + public Map backupValues(CodeStyleSettings settings, Language language) { + List baseValues = getFormattingValues(settings, language); + Map res = ContainerUtil.newHashMap(); + for (Value baseValue: baseValues) { + res.put(baseValue, baseValue.value); + } + return res; + } + + @NotNull + private Collection getVarKinds() { + List varKinds = new LinkedList(); + varKinds.addAll(myLangExtractor.getCustomVarKinds()); + varKinds.addAll(Arrays.asList(Value.VAR_KIND.defaultKinds)); + return varKinds; + } + + @NotNull + private Value.VAR_KIND getVarKind(@NotNull String name, @NotNull Object value) { + for (Value.VAR_KIND varKind : getVarKinds()) { + if (varKind.accepts(name, value)) { + return varKind; + } + } + return Value.VAR_KIND.NOTHING; + } + + @NotNull + private List readAll(@NotNull String instanceName, @NotNull Object instance) { + Class cls = instance.getClass(); + List ret = new ArrayList(); + ClassSerializer serializer = new ClassSerializer(instanceName, instance); + for (Field field : cls.getDeclaredFields()) { + field = ClassSerializer.getPreparedField(field); + if (field == null || field.getName().endsWith("_FORCE")) continue; + try { + ret.add(buildFValue(field, instance, serializer)); + } + catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + return ret; + } + + @NotNull + private Value buildFValue(@NotNull Field field, + @NotNull Object instance, + @NotNull ClassSerializer serializer) throws IllegalAccessException { + String name = field.getName(); + Object value = field.get(instance); + Value.VAR_KIND varKind = getVarKind(name, value); + return new Value(name, value, serializer, varKind); + } + + @NotNull + protected List getFormattingValues(CodeStyleSettings settings, Language language) { + + final CommonCodeStyleSettings commonSettings = settings.getCommonSettings(language); + CommonCodeStyleSettings.IndentOptions indentOptions = commonSettings.getIndentOptions(); + if (indentOptions == null) { + Utils.logError("IndentOptions from common settings are null; using indent options from settings."); + indentOptions = settings.getIndentOptions(); + } + final Object languageSettings = Utils.getLanguageSettings(settings, language); + + List values = readAll("commonSettings", commonSettings); + if (languageSettings != null) { + values.addAll(readAll("ocSettings", languageSettings)); + } + final LanguageCodeStyleSettingsProvider provider = LanguageCodeStyleSettingsProvider.forLanguage(language); + if (provider != null) { + final Set supportedFields = provider.getSupportedFields(); + List cvalues = new ArrayList(values.size()); + for (Value value : values) { + if (supportedFields.contains(value.name)) { + cvalues.add(value); + } + } + values = cvalues; + } + + if (indentOptions != null) { + List valuesOrder = readAll("indentOptions", indentOptions); + valuesOrder.addAll(values); + return valuesOrder; + } else { + Utils.logError("Not indent options detected."); + return values; + } + } + +} diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/processor/GenProcessor.java b/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/processor/GenProcessor.java new file mode 100644 index 000000000000..66d695a3bfd3 --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/processor/GenProcessor.java @@ -0,0 +1,67 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.codeStyle.extractor.processor; + +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiFile; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.extractor.Utils; +import com.intellij.psi.codeStyle.extractor.differ.Differ; +import com.intellij.psi.codeStyle.extractor.differ.LangCodeStyleExtractor; +import com.intellij.psi.codeStyle.extractor.values.Gens; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * @author Roman.Shein + * @since 29.07.2015. + */ +public class GenProcessor extends CodeStyleDeriveProcessor { + + private static DateFormat formatter = new SimpleDateFormat("mm:ss"); + + public GenProcessor(LangCodeStyleExtractor langExtractor) { + super(langExtractor); + } + + @Override + public Gens runWithProgress(Project project, CodeStyleSettings settings, PsiFile file, ProgressIndicator indicator) { + final Gens origGens = new Gens(getFormattingValues(settings, file.getLanguage())); + final Gens forSelection = origGens.copy(); + + final Differ differ = myLangExtractor.getDiffer(project, file, settings); + forSelection.dropToInitial(); + Utils.resetRandom(); + + long startTime = System.nanoTime(); + Utils.adjustValuesGA(forSelection, differ, indicator); + reportResult("GA", forSelection, differ, startTime, file.getName()); + + startTime = System.nanoTime(); + Utils.adjustValuesMin(forSelection, differ, indicator); + reportResult("MIN", forSelection, differ, startTime, file.getName()); + + return forSelection; + } + + private void reportResult(String label, Gens gens, Differ differ, long startTime, String fileName) { + Date date = new Date((System.nanoTime() - startTime) / 1000000); + System.out.println(fileName + ": " + label + " range:" + differ.getDifference(gens) + " Execution Time: " + formatter.format(date)); + } +} diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/values/ClassSerializer.java b/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/values/ClassSerializer.java new file mode 100644 index 000000000000..0c2c63667de6 --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/values/ClassSerializer.java @@ -0,0 +1,107 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.codeStyle.extractor.values; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +public class ClassSerializer { + @NotNull + private String myInstanceName; + @NotNull + private final Object myInstance; + + public ClassSerializer(@NotNull String instanceName, @NotNull Object o) { + myInstanceName = instanceName; + myInstance = o; + } + + @Nullable + public Object read(@NotNull String name) { + try { + final Field field = getPreparedField(myInstance.getClass().getField(name)); + if (field == null) return null; + return field.get(myInstance); + } + catch (NoSuchFieldException e) { + e.printStackTrace(); + } + catch (IllegalAccessException e) { + e.printStackTrace(); + } + return null; + } + + @Nullable + @Contract("_, _, false -> null") + public Object write(@NotNull String name, @NotNull Object value, boolean retPrevValue) { + try { + final Field field = getPreparedField(myInstance.getClass().getField(name)); + if (field != null) { + Object ret = retPrevValue ? field.get(myInstance) : null; + field.set(myInstance, value); + return ret; + } + } + catch (NoSuchFieldException e) { + e.printStackTrace(); + } + catch (IllegalAccessException e) { + e.printStackTrace(); + } + return null; + } + + @Nullable + public static Field getPreparedField(Field field) { + field.setAccessible(true); + Class type = field.getType(); + if ((field.getModifiers() & Modifier.STATIC) != 0) { + return null; + } + if (type != int.class && type != boolean.class) { + return null; + } + if (field.getName().startsWith("my")) { + return null; + } + return field; + } + + @NotNull + public String getInstanceName() { + return myInstanceName; + } + + @Override + public boolean equals(Object o) { + if (o instanceof ClassSerializer) { + ClassSerializer other = (ClassSerializer) o; + return other.myInstance.equals(myInstance); + } else { + return false; + } + } + + @Override + public int hashCode() { + return myInstance.hashCode(); + } +} diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/values/Generation.java b/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/values/Generation.java new file mode 100644 index 000000000000..d82476cf79d2 --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/values/Generation.java @@ -0,0 +1,145 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.codeStyle.extractor.values; + +import com.intellij.openapi.util.Pair; +import com.intellij.psi.codeStyle.extractor.Utils; +import com.intellij.psi.codeStyle.extractor.differ.Differ; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +public class Generation { + + private static int GENERATION_POOL_SIZE = 45; + private static int MUTATION_PER_GEN = 10; + public static int GEN_COUNT = 40; + private List myGensPool; + private int myAge; + private int myParentKind; + + private Generation(@NotNull final Gens bestGens) { + myParentKind = -1; + myGensPool = new ArrayList(GENERATION_POOL_SIZE); + for (int i = 0; i < GENERATION_POOL_SIZE; ++i) { + // the best goes as is + myGensPool.add(new Gens(bestGens).mutate(i == 0 ? 0 : MUTATION_PER_GEN)); + } + myAge = 0; + } + + private Generation(@NotNull Generation previous, int parentKind) { + myAge = previous.myAge + 1; + myParentKind = parentKind; + myGensPool = new ArrayList(GENERATION_POOL_SIZE); + int mutationsCount = MUTATION_PER_GEN; + //if (myAge < 30) { + // if (myAge < 5) { + // mutationsCount = 50; + // } + // else if (myAge % 5 == 0) { + // mutationsCount = 25; + // } + //} + //else if (myAge < 40) { + // mutationsCount = 10; + //} + //else if (myAge < 50) { + // mutationsCount = 5; + //} + + final int prevPullSize = previous.myGensPool.size(); + for (int i = 0; i < GENERATION_POOL_SIZE; i++) { + int parent1 = 0, parent2 = 0, iterations = 0; + while (parent1 == parent2) { + parent1 = Utils.getRandomLess(prevPullSize);//~ fitness? + parent2 = Utils.getRandomLess(prevPullSize); + if (++iterations > 25) break; + } + myGensPool.add(Gens.breed( + previous.myGensPool.get(parent1), + previous.myGensPool.get(parent2), + mutationsCount)); + } + } + + public static Generation createZeroGeneration(@NotNull Gens gens) { + return new Generation(gens); + } + + public static Generation createNextGeneration(Differ differ, @NotNull Generation previous) { + final int parentKind = previous.reduceToSize(differ, (int)(0.2 * previous.myGensPool.size())); + return previous.tryAgain() ? new Generation(previous, parentKind) : previous; + } + + private int reduceToSize(Differ differ, int newPoolSize) { + List> ranges = new ArrayList>(myGensPool.size()); + + int i = 0; + for (final Gens gens : myGensPool) { + int range = differ.getDifference(gens); + ranges.add(Pair.create(range, i++)); + if (range == 0) { + myAge = GEN_COUNT; + newPoolSize = 1; + break; + } + } + + Collections.sort(ranges, new Comparator>() { + @Override + public int compare(Pair o1, Pair o2) { + return o1.first - o2.first; + } + }); + + final ArrayList gensPool = new ArrayList(newPoolSize); + int count = 0; + int worseForward = 0; + for (final Pair pair : ranges) { + if (count >= newPoolSize) { + break; + } + Gens gens = myGensPool.get(pair.second); + gensPool.add(gens); + ++count; + worseForward = pair.first; + } + myGensPool = gensPool; + + return worseForward; + } + + public boolean tryAgain() { + return myAge < GEN_COUNT; + } + + public Gens getBestGens(Differ differ) { + reduceToSize(differ, 1); + return myGensPool.get(0); + } + + public int getAge() { + return myAge; + } + + public int getParentKind() { + return myParentKind; + } +} diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/values/Gens.java b/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/values/Gens.java new file mode 100644 index 000000000000..1384164dbbbd --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/values/Gens.java @@ -0,0 +1,126 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.codeStyle.extractor.values; + +import com.intellij.psi.codeStyle.extractor.Utils; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public class Gens extends ValuesExtractionResultImpl { + + public Gens(@NotNull List values) { + super(values); + } + + public Gens(@NotNull Gens gens) { + this(gens.copy().getValues()); + } + + public Gens mutate(int averageMutationCount) { + if (averageMutationCount > 0) { + // with mutation + + int commonMutagen = 0; + + for (Value value : myValues) { + commonMutagen += value.getMutagenFactor(); + } + + for (Value value : myValues) { + if (Utils.getRandomLess(commonMutagen) < value.getMutagenFactor() * averageMutationCount) { + final Object[] possibleValues = value.getPossibleValues(); + value.value = possibleValues[Utils.getRandomLess(possibleValues.length)]; + } + } + } + return this; + } + + public Gens dropToInitial() { + final ArrayList values = new ArrayList(); + for (Value value : myValues) { + final Object[] possibleValues = value.getPossibleValues(); + if (possibleValues.length > 0) { + value.value = possibleValues[0]; + } + } + return this; + } + + @NotNull + public Gens copy() { + final ArrayList values = new ArrayList(); + for (Value value : myValues) { + values.add(new Value(value)); + } + return new Gens(values); + } + + + public Gens diff(Gens newGens) { + final List newValues = newGens.getValues(); + final int size = myValues.size(); + assert size == newValues.size(); + final List diff = new ArrayList(); + for (int i = 0; i < size; ++i) { + final Value value = myValues.get(i); + final Value newValue = newValues.get(i); + assert value.name.equals(newValue.name); + if (!value.value.equals(newValue.value)) { + diff.add(new Value(value.name, value.value + "->" + newValue.value, value.serializer, value.kind)); + } + } + return new Gens(diff); + } + + @NotNull + public static Gens breed(@NotNull Gens p1, @NotNull Gens p2, int averageMutationCount) { + final int size = p1.myValues.size(); + assert size == p2.myValues.size(); + + // Crossover! + final int crossover = size / 2;//FUtils.getRandomLess(size - 6) + 3; + + final List values = new ArrayList(size); + for (int i = 0; i < size; ++i) { + final Value value1 = p1.myValues.get(i); + final Value value2 = p2.myValues.get(i); + if (value1.kind == Value.VAR_KIND.INDENT) { + values.add(new Value(value1)); + } + else if (value1.kind == Value.VAR_KIND.BRACE_STYLE) { + values.add(new Value(value2)); + } + else if ((i & 0x1) == 1) { + values.add(new Value(value1)); + } + else { + values.add(new Value(value2)); + } + } + return new Gens(values).mutate(averageMutationCount); + } + + public void copyFrom(Gens gens) { + if (myValues == gens.myValues) { + return; + } + myValues.clear(); + myValues.addAll(gens.myValues); + } +} diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/values/Value.java b/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/values/Value.java new file mode 100644 index 000000000000..152d89aec90f --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/values/Value.java @@ -0,0 +1,242 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.codeStyle.extractor.values; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class Value { + public enum STATE { + INIT, + SELECTED, + ANY + } + + public static abstract class VAR_KIND { + + public int getMutagenFactor() { + return 1; + } + + @NotNull + private Object[] myPossibleValues; + + VAR_KIND(@NotNull Object[] possibleValues) { + myPossibleValues = possibleValues; + } + + @NotNull + public Object[] getPossibleValues() { + return myPossibleValues; + } + + public abstract boolean accepts(@NotNull String name, @NotNull Object value); + + @NotNull + private static Object[] getRMValues() { + int from = 10; + int to = 120; + Object[] ret = new Object[to - from + 1]; + ret[0] = -1; //any + for (int i = to; i > from; --i) ret[i - from] = i; + return ret; + } + + //----------------Default var kinds go here--------------------- + public static final VAR_KIND RIGHT_MARGIN = new CLASS_BASED_VAR_KIND(getRMValues(), Integer.class) { + @Override + public boolean acceptsName(@NotNull String name) { + return name.equals("RIGHT_MARGIN"); + } + @Override + public int getMutagenFactor() { + return 0; + } + }; + + public static final VAR_KIND BLANK = new CLASS_BASED_VAR_KIND(new Object[]{0, 1, 2}, Integer.class) { + @Override + public boolean acceptsName(@NotNull String name) { + return name.contains("BLANK"); + } + @Override + public int getMutagenFactor() { + return 2; + } + }; + + public static final VAR_KIND INDENT = new CLASS_BASED_VAR_KIND(new Object[]{0, 1, 2, 3, 4, 5, 6, 7, 8}, Integer.class) { + @Override + public boolean acceptsName(@NotNull String name) { + return name.contains("INDENT"); + } + @Override + public int getMutagenFactor() { + return 7; + } + }; + + public static final VAR_KIND DEFAULT = new CLASS_BASED_VAR_KIND(new Object[]{0, 1, 2, 3, 4, 5, 6, 7, 8}, Integer.class) { + @Override + public boolean acceptsName(@NotNull String name) { + return true; + } + }; + + public static final VAR_KIND TAB_SIZE = new CLASS_BASED_VAR_KIND(new Object[]{2, 4, 8}, Integer.class) { + @Override + public boolean acceptsName(@NotNull String name) { + return name.contains("TAB_SIZE"); + } + @Override + public int getMutagenFactor() { + return 2; + } + }; + + public static final VAR_KIND WRAP = new CLASS_BASED_VAR_KIND(new Object[]{0, 1, 2, 5}, Integer.class) { + @Override + public boolean acceptsName(@NotNull String name) { + return name.endsWith("_WRAP"); + } + }; + + public static final VAR_KIND BRACE_STYLE = new CLASS_BASED_VAR_KIND(new Object[]{1, 2, 3, 4, 5}, Integer.class) { + @Override + public boolean acceptsName(@NotNull String name) { + return name.endsWith("BRACE_STYLE") || name.endsWith("BRACE_PLACEMENT"); + } + @Override + public int getMutagenFactor() { + return 7; + } + }; + + public static final VAR_KIND KEEP = new CLASS_BASED_VAR_KIND(new Object[]{true, false}, Boolean.class) { + @Override + public boolean acceptsName(@NotNull String name) { + return name.contains("KEEP"); + } + @Override + public int getMutagenFactor() { + return 3; + } + }; + + public static final VAR_KIND BOOL = new CLASS_BASED_VAR_KIND(new Object[]{true, false}, Boolean.class) { + @Override + public boolean acceptsName(@NotNull String name) { + return true; + } + }; + + public static final VAR_KIND NOTHING = new VAR_KIND(new Object[]{}) { + @Override + public boolean accepts(@NotNull String name, @NotNull Object value) { + return true; + } + }; + + public static final VAR_KIND[] defaultKinds = {RIGHT_MARGIN, WRAP, BRACE_STYLE, TAB_SIZE, INDENT, BLANK, DEFAULT, + KEEP, BOOL, NOTHING}; + } + + public static abstract class CLASS_BASED_VAR_KIND extends VAR_KIND { + + private final Class myVarClass; + + public CLASS_BASED_VAR_KIND(@NotNull Object[] possibleValues, @NotNull Class varClass) { + super(possibleValues); + myVarClass = varClass; + } + + @Override + public boolean accepts(@NotNull String name, @NotNull Object value) { + return value.getClass().equals(myVarClass) && acceptsName(name); + } + + public abstract boolean acceptsName(@NotNull String name); + } + + public Object[] getPossibleValues() { + return kind.getPossibleValues(); + } + + public int getMutagenFactor() { + return kind.getMutagenFactor(); + } + + @NotNull + public final String name; + @NotNull + public Object value; + @NotNull + public STATE state; + @NotNull + public ClassSerializer serializer; + @NotNull + public final VAR_KIND kind; + + public Value(@NotNull String name, @NotNull Object value, @NotNull ClassSerializer serializer, @NotNull VAR_KIND kind) { + this.kind = kind; + this.name = name; + if (value instanceof Integer && ((Integer)value) == 0 && kind == VAR_KIND.BRACE_STYLE) { + this.value = 1; + } + else { + this.value = value; + } + + state = STATE.INIT; + this.serializer = serializer; + } + + public Value(@NotNull Value valueZ) { + name = valueZ.name; + value = valueZ.value; + state = valueZ.state; + serializer = valueZ.serializer; + kind = valueZ.kind; + } + + @Nullable + @Contract("false -> null") + public Value write(boolean retPrevValue) { + final Object orig = serializer.write(name, value, retPrevValue); + return orig == null ? null : new Value(name, orig, serializer, kind); + } + + @Override + public String toString() { + return name + "=" + value + ";"; + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof Value) { + Value other = (Value) o; + return other.name.equals(name) && other.serializer.equals(serializer); + } else { + return false; + } + } +} diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/values/ValuesExtractionResult.java b/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/values/ValuesExtractionResult.java new file mode 100644 index 000000000000..a115f954571d --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/values/ValuesExtractionResult.java @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.codeStyle.extractor.values; + +import com.intellij.openapi.util.Condition; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; + +/** + * @author Roman.Shein + * @since 04.08.2015. + */ +public interface ValuesExtractionResult { + + @NotNull + List getValues(); + + void applySelected(); + + void applyConditioned(Condition c, Map backup); + + ValuesExtractionResult apply(boolean retPrevValue); +} diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/values/ValuesExtractionResultImpl.java b/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/values/ValuesExtractionResultImpl.java new file mode 100644 index 000000000000..f66d557a8270 --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/extractor/values/ValuesExtractionResultImpl.java @@ -0,0 +1,81 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.codeStyle.extractor.values; + +import com.intellij.openapi.util.Condition; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * @author Roman.Shein + * @since 04.08.2015. + */ +public class ValuesExtractionResultImpl implements ValuesExtractionResult { + @NotNull + protected final List myValues; + + public ValuesExtractionResultImpl(@NotNull List values) { + myValues = values; + } + + @NotNull + public List getValues() { + return myValues; + } + + public void applySelected() { + for (Value value : myValues) { + if (value.state == Value.STATE.SELECTED) { + value.write(false); + } + } + } + + @Override + public void applyConditioned(Condition c, Map backup) { + for (Value value: myValues) { + if (c.value(value)) { + value.write(false); + } else { + value.value = backup.get(value); + value.write(false); + } + } + } + + @Contract("false -> null") + public ValuesExtractionResult apply(boolean retPrevValue) { + if (retPrevValue) { + final ArrayList orig = new ArrayList(); + for (Value value : myValues) { + final Value old = value.write(true); + if (old != null) { + orig.add(old); + } + } + return new Gens(orig); + } + + for (Value value : myValues) { + value.write(false); + } + return null; + } +} diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/presentation/CodeStyleBoundedIntegerSettingPresentation.java b/platform/lang-api/src/com/intellij/psi/codeStyle/presentation/CodeStyleBoundedIntegerSettingPresentation.java new file mode 100644 index 000000000000..5837eca5ae5f --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/presentation/CodeStyleBoundedIntegerSettingPresentation.java @@ -0,0 +1,66 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.codeStyle.presentation; + +import org.jetbrains.annotations.NotNull; + +/** + * @author Roman.Shein + * @since 15.09.2015. + */ +public class CodeStyleBoundedIntegerSettingPresentation extends CodeStyleSettingPresentation { + + protected int myLowerBound; + protected int myUpperBound; + protected int myDefaultValue; + protected String myDefaultValueUiName; + + public CodeStyleBoundedIntegerSettingPresentation(@NotNull String fieldName, + @NotNull String uiName, + int lowerBound, + int upperBound, + int defaultValue, + String defaultValueUiName) { + super(fieldName, uiName); + myLowerBound = lowerBound; + myUpperBound = upperBound; + myDefaultValue = defaultValue; + myDefaultValueUiName = defaultValueUiName; + } + + public int getLowerBound() { + return myLowerBound; + } + + public int getUpperBound() { + return myUpperBound; + } + + public int getDefaultValue() { + return myDefaultValue; + } + + @Override + @NotNull + public String getValueUiName(@NotNull Object value) { + if (value instanceof Integer) { + int intValue = (Integer) value; + return intValue == myDefaultValue ? myDefaultValueUiName : super.getValueUiName(value); + } else { + return super.getValueUiName(value); + } + } +} diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/presentation/CodeStyleSelectSettingPresentation.java b/platform/lang-api/src/com/intellij/psi/codeStyle/presentation/CodeStyleSelectSettingPresentation.java new file mode 100644 index 000000000000..1380aa08080f --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/presentation/CodeStyleSelectSettingPresentation.java @@ -0,0 +1,84 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.codeStyle.presentation; + +import org.jetbrains.annotations.NotNull; + +/** + * @author Roman.Shein + * @since 16.09.2015. + */ +public class CodeStyleSelectSettingPresentation extends CodeStyleSettingPresentation { + + @NotNull + protected int[] myValues; + @NotNull + protected String[] myValueUiNames; + + protected int myLowerBound; + protected int myUpperBound; + + public CodeStyleSelectSettingPresentation(@NotNull String fieldName, @NotNull String uiName, + @NotNull int[] values, @NotNull String[] valueUiNames) { + super(fieldName, uiName); + + assert(values.length == valueUiNames.length); + assert(values.length > 0); + + myValues = values; + myValueUiNames = valueUiNames; + + //TODO get bounds more gracefully + myLowerBound = values[0]; + myUpperBound = values[0]; + for (int value : values) { + myLowerBound = Math.min(value, myLowerBound); + myUpperBound = Math.max(value, myUpperBound); + } + } + + @Override + @NotNull + public String getValueUiName(@NotNull Object value) { + if (value instanceof Integer) { + int intValue = (Integer) value; + for (int i = 0; i < myValues.length; ++i) { + if (myValues[i] == intValue) return myValueUiNames[i]; + } + return super.getValueUiName(value); + } else { + return super.getValueUiName(value); + } + } + + public int getLowerBound() { + return myLowerBound; + } + + public int getUpperBound() { + return myUpperBound; + } + + @NotNull + public int[] getValues() { + return myValues; + } + + @NotNull + public String[] getOptions() { + return myValueUiNames; + } +} diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/presentation/CodeStyleSettingPresentation.java b/platform/lang-api/src/com/intellij/psi/codeStyle/presentation/CodeStyleSettingPresentation.java new file mode 100644 index 000000000000..8cafca280569 --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/presentation/CodeStyleSettingPresentation.java @@ -0,0 +1,525 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.codeStyle.presentation; + +import com.intellij.openapi.application.ApplicationBundle; +import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.ContainerUtil.ImmutableMapBuilder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import static com.intellij.psi.codeStyle.CodeStyleSettingsCustomizable.*; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * @author Roman.Shein + * @since 15.09.2015. + */ +public class CodeStyleSettingPresentation { + + public static class SettingsGroup { + @Nullable + public final String name; + + public SettingsGroup(@Nullable String name) { + this.name = name; + } + + @Override + public boolean equals(Object o) { + if (o instanceof SettingsGroup) { + SettingsGroup other = (SettingsGroup) o; + return name != null && name.equals(other.name); + } else { + return false; + } + } + + @Override + public int hashCode() { + return name == null ? 0 : name.hashCode(); + } + + public boolean isNull() { + return name == null; + } + } + + @NotNull + protected String myFieldName; + + @NotNull + protected String myUiName; + + public CodeStyleSettingPresentation(@NotNull String fieldName, @NotNull String uiName) { + myFieldName = fieldName; + myUiName = uiName; + } + + @NotNull + public String getFieldName() { + return myFieldName; + } + + @NotNull + public String getUiName() { + return myUiName; + } + + public void setUiName(@NotNull String newName) { + myUiName = newName; + } + + @NotNull + public String getValueUiName(@NotNull Object value) { + return value.toString(); + } + + @Override + public boolean equals(Object o) { + return (o instanceof CodeStyleSettingPresentation) && ((CodeStyleSettingPresentation)o).getFieldName().equals(getFieldName()); + } + + @Override + public int hashCode() { + return myFieldName.hashCode(); + } + + protected static void putGroupTop(@NotNull Map> result, + @NotNull String fieldName, + @NotNull String uiName, int[] values, String[] valueUiNames) { + result.put(new SettingsGroup(null), ContainerUtil.immutableList( + new CodeStyleSelectSettingPresentation(fieldName, uiName, values, valueUiNames) + )); + } + + protected static final Map> BLANK_LINES_STANDARD_SETTINGS; + protected static final Map> SPACING_STANDARD_SETTINGS; + protected static final Map> WRAPPING_AND_BRACES_STANDARD_SETTINGS; + protected static final Map> INDENT_STANDARD_SETTINGS; + static { + + //-----------------------------------BLANK_LINES_SETTINGS----------------------------------------------------- + + Map> result = ContainerUtil.newLinkedHashMap(); + result.put(new SettingsGroup(BLANK_LINES_KEEP), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("KEEP_BLANK_LINES_IN_DECLARATIONS", + ApplicationBundle.message("editbox.keep.blanklines.in.declarations")), + new CodeStyleSettingPresentation("KEEP_BLANK_LINES_IN_CODE", ApplicationBundle.message("editbox.keep.blanklines.in.code")), + new CodeStyleSettingPresentation("KEEP_BLANK_LINES_BEFORE_RBRACE", + ApplicationBundle.message("editbox.keep.blanklines.before.rbrace")) + )); + + result.put(new SettingsGroup(BLANK_LINES), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("BLANK_LINES_BEFORE_PACKAGE", + ApplicationBundle.message("editbox.blanklines.before.package.statement")), + new CodeStyleSettingPresentation("BLANK_LINES_AFTER_PACKAGE", + ApplicationBundle.message("editbox.blanklines.after.package.statement")), + new CodeStyleSettingPresentation("BLANK_LINES_BEFORE_IMPORTS", ApplicationBundle.message("editbox.blanklines.before.imports")), + new CodeStyleSettingPresentation("BLANK_LINES_AFTER_IMPORTS", ApplicationBundle.message("editbox.blanklines.after.imports")), + new CodeStyleSettingPresentation("BLANK_LINES_AROUND_CLASS", ApplicationBundle.message("editbox.blanklines.around.class")), + new CodeStyleSettingPresentation("BLANK_LINES_AFTER_CLASS_HEADER", + ApplicationBundle.message("editbox.blanklines.after.class.header")), + new CodeStyleSettingPresentation("BLANK_LINES_AFTER_ANONYMOUS_CLASS_HEADER", + ApplicationBundle.message("editbox.blanklines.after.anonymous.class.header")), + new CodeStyleSettingPresentation("BLANK_LINES_AROUND_FIELD_IN_INTERFACE", "Around field in interface:"), + //TODO why is this not loaded from bundle?? + new CodeStyleSettingPresentation("BLANK_LINES_AROUND_FIELD", ApplicationBundle.message("editbox.blanklines.around.field")), + new CodeStyleSettingPresentation("BLANK_LINES_AROUND_METHOD_IN_INTERFACE", "Around method in interface:"), + //TODO why is this not loaded from bundle?? + new CodeStyleSettingPresentation("BLANK_LINES_AROUND_METHOD", ApplicationBundle.message("editbox.blanklines.around.method")), + new CodeStyleSettingPresentation("BLANK_LINES_BEFORE_METHOD_BODY", + ApplicationBundle.message("editbox.blanklines.before.method.body")) + )); + BLANK_LINES_STANDARD_SETTINGS = Collections.unmodifiableMap(result); + + //-----------------------------------SPACING_SETTINGS----------------------------------------------------- + + result = ContainerUtil.newLinkedHashMap(); + result.put(new SettingsGroup(SPACES_BEFORE_PARENTHESES), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("SPACE_BEFORE_METHOD_PARENTHESES", + ApplicationBundle.message("checkbox.spaces.method.declaration.parentheses")), + new CodeStyleSettingPresentation("SPACE_BEFORE_METHOD_CALL_PARENTHESES", + ApplicationBundle.message("checkbox.spaces.method.call.parentheses")), + new CodeStyleSettingPresentation("SPACE_BEFORE_IF_PARENTHESES", ApplicationBundle.message("checkbox.spaces.if.parentheses")), + new CodeStyleSettingPresentation("SPACE_BEFORE_FOR_PARENTHESES", ApplicationBundle.message("checkbox.spaces.for.parentheses")), + new CodeStyleSettingPresentation("SPACE_BEFORE_WHILE_PARENTHESES", + ApplicationBundle.message("checkbox.spaces.while.parentheses")), + new CodeStyleSettingPresentation("SPACE_BEFORE_SWITCH_PARENTHESES", + ApplicationBundle.message("checkbox.spaces.switch.parentheses")), + new CodeStyleSettingPresentation("SPACE_BEFORE_TRY_PARENTHESES", ApplicationBundle.message("checkbox.spaces.try.parentheses")), + new CodeStyleSettingPresentation("SPACE_BEFORE_CATCH_PARENTHESES", + ApplicationBundle.message("checkbox.spaces.catch.parentheses")), + new CodeStyleSettingPresentation("SPACE_BEFORE_SYNCHRONIZED_PARENTHESES", + ApplicationBundle.message("checkbox.spaces.synchronized.parentheses")), + new CodeStyleSettingPresentation("SPACE_BEFORE_ANOTATION_PARAMETER_LIST", + ApplicationBundle.message("checkbox.spaces.annotation.parameters")) + )); + + result.put(new SettingsGroup(SPACES_AROUND_OPERATORS), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("SPACE_AROUND_ASSIGNMENT_OPERATORS", + ApplicationBundle.message("checkbox.spaces.assignment.operators")), + new CodeStyleSettingPresentation("SPACE_AROUND_LOGICAL_OPERATORS", + ApplicationBundle.message("checkbox.spaces.logical.operators")), + new CodeStyleSettingPresentation("SPACE_AROUND_EQUALITY_OPERATORS", + ApplicationBundle.message("checkbox.spaces.equality.operators")), + new CodeStyleSettingPresentation("SPACE_AROUND_RELATIONAL_OPERATORS", + ApplicationBundle.message("checkbox.spaces.relational.operators")), + new CodeStyleSettingPresentation("SPACE_AROUND_BITWISE_OPERATORS", + ApplicationBundle.message("checkbox.spaces.bitwise.operators")), + new CodeStyleSettingPresentation("SPACE_AROUND_ADDITIVE_OPERATORS", + ApplicationBundle.message("checkbox.spaces.additive.operators")), + new CodeStyleSettingPresentation("SPACE_AROUND_MULTIPLICATIVE_OPERATORS", + ApplicationBundle.message("checkbox.spaces.multiplicative.operators")), + new CodeStyleSettingPresentation("SPACE_AROUND_SHIFT_OPERATORS", ApplicationBundle.message("checkbox.spaces.shift.operators")), + new CodeStyleSettingPresentation("SPACE_AROUND_UNARY_OPERATOR", + ApplicationBundle.message("checkbox.spaces.around.unary.operator")), + new CodeStyleSettingPresentation("SPACE_AROUND_LAMBDA_ARROW", ApplicationBundle.message("checkbox.spaces.around.lambda.arrow")), + new CodeStyleSettingPresentation("SPACE_AROUND_METHOD_REF_DBL_COLON", + ApplicationBundle.message("checkbox.spaces.around.method.ref.dbl.colon.arrow")) + )); + + result.put(new SettingsGroup(SPACES_BEFORE_LEFT_BRACE), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("SPACE_BEFORE_CLASS_LBRACE", ApplicationBundle.message("checkbox.spaces.class.left.brace")), + new CodeStyleSettingPresentation("SPACE_BEFORE_METHOD_LBRACE", ApplicationBundle.message("checkbox.spaces.method.left.brace")), + new CodeStyleSettingPresentation("SPACE_BEFORE_IF_LBRACE", ApplicationBundle.message("checkbox.spaces.if.left.brace")), + new CodeStyleSettingPresentation("SPACE_BEFORE_ELSE_LBRACE", ApplicationBundle.message("checkbox.spaces.else.left.brace")), + new CodeStyleSettingPresentation("SPACE_BEFORE_FOR_LBRACE", ApplicationBundle.message("checkbox.spaces.for.left.brace")), + new CodeStyleSettingPresentation("SPACE_BEFORE_WHILE_LBRACE", ApplicationBundle.message("checkbox.spaces.while.left.brace")), + new CodeStyleSettingPresentation("SPACE_BEFORE_DO_LBRACE", ApplicationBundle.message("checkbox.spaces.do.left.brace")), + new CodeStyleSettingPresentation("SPACE_BEFORE_SWITCH_LBRACE", ApplicationBundle.message("checkbox.spaces.switch.left.brace")), + new CodeStyleSettingPresentation("SPACE_BEFORE_TRY_LBRACE", ApplicationBundle.message("checkbox.spaces.try.left.brace")), + new CodeStyleSettingPresentation("SPACE_BEFORE_CATCH_LBRACE", ApplicationBundle.message("checkbox.spaces.catch.left.brace")), + new CodeStyleSettingPresentation("SPACE_BEFORE_FINALLY_LBRACE", + ApplicationBundle.message("checkbox.spaces.finally.left.brace")), + new CodeStyleSettingPresentation("SPACE_BEFORE_SYNCHRONIZED_LBRACE", + ApplicationBundle.message("checkbox.spaces.synchronized.left.brace")), + new CodeStyleSettingPresentation("SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE", + ApplicationBundle.message("checkbox.spaces.array.initializer.left.brace")), + new CodeStyleSettingPresentation("SPACE_BEFORE_ANNOTATION_ARRAY_INITIALIZER_LBRACE", + ApplicationBundle.message("checkbox.spaces.annotation.array.initializer.left.brace")) + )); + + result.put(new SettingsGroup(SPACES_BEFORE_KEYWORD), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("SPACE_BEFORE_ELSE_KEYWORD", ApplicationBundle.message("checkbox.spaces.else.keyword")), + new CodeStyleSettingPresentation("SPACE_BEFORE_WHILE_KEYWORD", ApplicationBundle.message("checkbox.spaces.while.keyword")), + new CodeStyleSettingPresentation("SPACE_BEFORE_CATCH_KEYWORD", ApplicationBundle.message("checkbox.spaces.catch.keyword")), + new CodeStyleSettingPresentation("SPACE_BEFORE_FINALLY_KEYWORD", ApplicationBundle.message("checkbox.spaces.finally.keyword")) + )); + + result.put(new SettingsGroup(SPACES_WITHIN), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("SPACE_WITHIN_BRACES", ApplicationBundle.message("checkbox.spaces.within.braces")), + new CodeStyleSettingPresentation("SPACE_WITHIN_BRACKETS", ApplicationBundle.message("checkbox.spaces.within.brackets")), + new CodeStyleSettingPresentation("SPACE_WITHIN_ARRAY_INITIALIZER_BRACES", + ApplicationBundle.message("checkbox.spaces.within.array.initializer.braces")), + new CodeStyleSettingPresentation("SPACE_WITHIN_EMPTY_ARRAY_INITIALIZER_BRACES", + ApplicationBundle.message("checkbox.spaces.within.empty.array.initializer.braces")), + new CodeStyleSettingPresentation("SPACE_WITHIN_PARENTHESES", ApplicationBundle.message("checkbox.spaces.within.parentheses")), + new CodeStyleSettingPresentation("SPACE_WITHIN_METHOD_CALL_PARENTHESES", + ApplicationBundle.message("checkbox.spaces.checkbox.spaces.method.call.parentheses")), + new CodeStyleSettingPresentation("SPACE_WITHIN_EMPTY_METHOD_CALL_PARENTHESES", + ApplicationBundle.message("checkbox.spaces.checkbox.spaces.empty.method.call.parentheses")), + new CodeStyleSettingPresentation("SPACE_WITHIN_METHOD_PARENTHESES", + ApplicationBundle.message("checkbox.spaces.checkbox.spaces.method.declaration.parentheses")), + new CodeStyleSettingPresentation("SPACE_WITHIN_EMPTY_METHOD_PARENTHESES", ApplicationBundle + .message("checkbox.spaces.checkbox.spaces.empty.method.declaration.parentheses")), + new CodeStyleSettingPresentation("SPACE_WITHIN_IF_PARENTHESES", ApplicationBundle.message("checkbox.spaces.if.parentheses")), + new CodeStyleSettingPresentation("SPACE_WITHIN_FOR_PARENTHESES", ApplicationBundle.message("checkbox.spaces.for.parentheses")), + new CodeStyleSettingPresentation("SPACE_WITHIN_WHILE_PARENTHESES", + ApplicationBundle.message("checkbox.spaces.while.parentheses")), + new CodeStyleSettingPresentation("SPACE_WITHIN_SWITCH_PARENTHESES", + ApplicationBundle.message("checkbox.spaces.switch.parentheses")), + new CodeStyleSettingPresentation("SPACE_WITHIN_TRY_PARENTHESES", ApplicationBundle.message("checkbox.spaces.try.parentheses")), + new CodeStyleSettingPresentation("SPACE_WITHIN_CATCH_PARENTHESES", + ApplicationBundle.message("checkbox.spaces.catch.parentheses")), + new CodeStyleSettingPresentation("SPACE_WITHIN_SYNCHRONIZED_PARENTHESES", + ApplicationBundle.message("checkbox.spaces.synchronized.parentheses")), + new CodeStyleSettingPresentation("SPACE_WITHIN_CAST_PARENTHESES", + ApplicationBundle.message("checkbox.spaces.type.cast.parentheses")), + new CodeStyleSettingPresentation("SPACE_WITHIN_ANNOTATION_PARENTHESES", + ApplicationBundle.message("checkbox.spaces.annotation.parentheses")) + )); + + result.put(new SettingsGroup(SPACES_IN_TERNARY_OPERATOR), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("SPACE_BEFORE_QUEST", ApplicationBundle.message("checkbox.spaces.before.question")), + new CodeStyleSettingPresentation("SPACE_AFTER_QUEST", ApplicationBundle.message("checkbox.spaces.after.question")), + new CodeStyleSettingPresentation("SPACE_BEFORE_COLON", ApplicationBundle.message("checkbox.spaces.before.colon")), + new CodeStyleSettingPresentation("SPACE_AFTER_COLON", ApplicationBundle.message("checkbox.spaces.after.colon")) + )); + + result.put(new SettingsGroup(SPACES_WITHIN_TYPE_ARGUMENTS), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("SPACE_AFTER_COMMA_IN_TYPE_ARGUMENTS", + ApplicationBundle.message("checkbox.spaces.after.comma")) + )); + + result.put(new SettingsGroup(SPACES_IN_TYPE_ARGUMENTS), ContainerUtil.immutableList()); + + result.put(new SettingsGroup(SPACES_IN_TYPE_PARAMETERS), ContainerUtil.immutableList()); + + result.put(new SettingsGroup(SPACES_OTHER), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("SPACE_BEFORE_COMMA", ApplicationBundle.message("checkbox.spaces.before.comma")), + new CodeStyleSettingPresentation("SPACE_AFTER_COMMA", ApplicationBundle.message("checkbox.spaces.after.comma")), + new CodeStyleSettingPresentation("SPACE_BEFORE_SEMICOLON", ApplicationBundle.message("checkbox.spaces.before.semicolon")), + new CodeStyleSettingPresentation("SPACE_AFTER_SEMICOLON", ApplicationBundle.message("checkbox.spaces.after.semicolon")), + new CodeStyleSettingPresentation("SPACE_AFTER_TYPE_CAST", ApplicationBundle.message("checkbox.spaces.after.type.cast")) + )); + SPACING_STANDARD_SETTINGS = Collections.unmodifiableMap(result); + + //-----------------------------------WRAPPING_AND_BRACES_SETTINGS----------------------------------------------------- + + result = ContainerUtil.newLinkedHashMap(); + result.put(new SettingsGroup(null), ContainerUtil.immutableList( + new CodeStyleBoundedIntegerSettingPresentation("RIGHT_MARGIN", ApplicationBundle.message("editbox.right.margin.columns"), 0, 999, + -1, + ApplicationBundle.message("settings.code.style.default.general")) + )); + + putGroupTop(result, "WRAP_ON_TYPING", ApplicationBundle.message("wrapping.wrap.on.typing"), WRAP_ON_TYPING_VALUES, + WRAP_ON_TYPING_OPTIONS); + + result.put(new SettingsGroup(WRAPPING_KEEP), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("KEEP_LINE_BREAKS", ApplicationBundle.message("wrapping.keep.line.breaks")), + new CodeStyleSettingPresentation("KEEP_FIRST_COLUMN_COMMENT", + ApplicationBundle.message("wrapping.keep.comment.at.first.column")), + new CodeStyleSettingPresentation("KEEP_CONTROL_STATEMENT_IN_ONE_LINE", + ApplicationBundle.message("checkbox.keep.when.reformatting.control.statement.in.one.line")), + new CodeStyleSettingPresentation("KEEP_MULTIPLE_EXPRESSIONS_IN_ONE_LINE", + ApplicationBundle.message("wrapping.keep.multiple.expressions.in.one.line")), + new CodeStyleSettingPresentation("KEEP_SIMPLE_BLOCKS_IN_ONE_LINE", + ApplicationBundle.message("wrapping.keep.simple.blocks.in.one.line")), + new CodeStyleSettingPresentation("KEEP_SIMPLE_METHODS_IN_ONE_LINE", + ApplicationBundle.message("wrapping.keep.simple.methods.in.one.line")), + new CodeStyleSettingPresentation("KEEP_SIMPLE_CLASSES_IN_ONE_LINE", + ApplicationBundle.message("wrapping.keep.simple.classes.in.one.line")) + )); + + result.put(new SettingsGroup(null), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("WRAP_LONG_LINES", ApplicationBundle.message("wrapping.long.lines")) + )); + + result.put(new SettingsGroup(WRAPPING_COMMENTS), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("WRAP_COMMENTS", ApplicationBundle.message("wrapping.comments.wrap.at.right.margin")) + )); + + result.put(new SettingsGroup(WRAPPING_BRACES), ContainerUtil.immutableList( + new CodeStyleSelectSettingPresentation("CLASS_BRACE_STYLE", + ApplicationBundle.message("wrapping.brace.placement.class.declaration"), + BRACE_PLACEMENT_VALUES, BRACE_PLACEMENT_OPTIONS), + new CodeStyleSelectSettingPresentation("METHOD_BRACE_STYLE", + ApplicationBundle.message("wrapping.brace.placement.method.declaration"), + BRACE_PLACEMENT_VALUES, BRACE_PLACEMENT_OPTIONS), + new CodeStyleSelectSettingPresentation("BRACE_STYLE", ApplicationBundle.message("wrapping.brace.placement.other"), + BRACE_PLACEMENT_VALUES, BRACE_PLACEMENT_OPTIONS) + + )); + + putGroupTop(result, "EXTENDS_LIST_WRAP", WRAPPING_EXTENDS_LIST, WRAP_VALUES, WRAP_OPTIONS); + result.put(new SettingsGroup(WRAPPING_EXTENDS_LIST), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("ALIGN_MULTILINE_EXTENDS_LIST", ApplicationBundle.message("wrapping.align.when.multiline")) + )); + + putGroupTop(result, "EXTENDS_KEYWORD_WRAP", WRAPPING_EXTENDS_KEYWORD, WRAP_VALUES_FOR_SINGLETON, WRAP_OPTIONS_FOR_SINGLETON); + + putGroupTop(result, "THROWS_LIST_WRAP", WRAPPING_THROWS_LIST, WRAP_VALUES, WRAP_OPTIONS); + result.put(new SettingsGroup(WRAPPING_THROWS_LIST), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("ALIGN_MULTILINE_THROWS_LIST", ApplicationBundle.message("wrapping.align.when.multiline")), + new CodeStyleSettingPresentation("ALIGN_THROWS_KEYWORD", ApplicationBundle.message("wrapping.align.throws.keyword")) + )); + + putGroupTop(result, "THROWS_KEYWORD_WRAP", WRAPPING_THROWS_KEYWORD, WRAP_VALUES_FOR_SINGLETON, WRAP_OPTIONS_FOR_SINGLETON); + + putGroupTop(result, "METHOD_PARAMETERS_WRAP", WRAPPING_METHOD_PARAMETERS, WRAP_VALUES, WRAP_OPTIONS); + result.put(new SettingsGroup(WRAPPING_METHOD_PARAMETERS), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("ALIGN_MULTILINE_PARAMETERS", ApplicationBundle.message("wrapping.align.when.multiline")), + new CodeStyleSettingPresentation("METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE", + ApplicationBundle.message("wrapping.new.line.after.lpar")), + new CodeStyleSettingPresentation("METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE", + ApplicationBundle.message("wrapping.rpar.on.new.line")) + )); + + putGroupTop(result, "CALL_PARAMETERS_WRAP", WRAPPING_METHOD_ARGUMENTS_WRAPPING, WRAP_VALUES, WRAP_OPTIONS); + result.put(new SettingsGroup(WRAPPING_METHOD_ARGUMENTS_WRAPPING), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("ALIGN_MULTILINE_PARAMETERS_IN_CALLS", + ApplicationBundle.message("wrapping.align.when.multiline")), + new CodeStyleSettingPresentation("PREFER_PARAMETERS_WRAP", + ApplicationBundle.message("wrapping.take.priority.over.call.chain.wrapping")), + new CodeStyleSettingPresentation("CALL_PARAMETERS_LPAREN_ON_NEXT_LINE", + ApplicationBundle.message("wrapping.new.line.after.lpar")), + new CodeStyleSettingPresentation("CALL_PARAMETERS_RPAREN_ON_NEXT_LINE", ApplicationBundle.message("wrapping.rpar.on.new.line")) + )); + + result.put(new SettingsGroup(WRAPPING_METHOD_PARENTHESES), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("ALIGN_MULTILINE_METHOD_BRACKETS", ApplicationBundle.message("wrapping.align.when.multiline")) + )); + + putGroupTop(result, "METHOD_CALL_CHAIN_WRAP", WRAPPING_CALL_CHAIN, WRAP_VALUES, WRAP_OPTIONS); + result.put(new SettingsGroup(WRAPPING_CALL_CHAIN), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("WRAP_FIRST_METHOD_IN_CALL_CHAIN", + ApplicationBundle.message("wrapping.chained.method.call.first.on.new.line")), + new CodeStyleSettingPresentation("ALIGN_MULTILINE_CHAINED_METHODS", ApplicationBundle.message("wrapping.align.when.multiline")) + )); + + result.put(new SettingsGroup(WRAPPING_IF_STATEMENT), ContainerUtil.immutableList( + new CodeStyleSelectSettingPresentation("IF_BRACE_FORCE", ApplicationBundle.message("wrapping.force.braces"), BRACE_VALUES, + BRACE_OPTIONS), + new CodeStyleSettingPresentation("ELSE_ON_NEW_LINE", ApplicationBundle.message("wrapping.else.on.new.line")), + new CodeStyleSettingPresentation("SPECIAL_ELSE_IF_TREATMENT", + ApplicationBundle.message("wrapping.special.else.if.braces.treatment")) + )); + + putGroupTop(result, "FOR_STATEMENT_WRAP", WRAPPING_FOR_STATEMENT, WRAP_VALUES, WRAP_OPTIONS); + result.put(new SettingsGroup(WRAPPING_FOR_STATEMENT), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("ALIGN_MULTILINE_FOR", ApplicationBundle.message("wrapping.align.when.multiline")), + new CodeStyleSettingPresentation("FOR_STATEMENT_LPAREN_ON_NEXT_LINE", + ApplicationBundle.message("wrapping.new.line.after.lpar")), + new CodeStyleSettingPresentation("FOR_STATEMENT_RPAREN_ON_NEXT_LINE", ApplicationBundle.message("wrapping.rpar.on.new.line")), + new CodeStyleSelectSettingPresentation("FOR_BRACE_FORCE", ApplicationBundle.message("wrapping.force.braces"), BRACE_VALUES, + BRACE_OPTIONS) + )); + + result.put(new SettingsGroup(WRAPPING_WHILE_STATEMENT), ContainerUtil.immutableList( + new CodeStyleSelectSettingPresentation("WHILE_BRACE_FORCE", ApplicationBundle.message("wrapping.force.braces"), BRACE_VALUES, + BRACE_OPTIONS) + )); + + result.put(new SettingsGroup(WRAPPING_DOWHILE_STATEMENT), ContainerUtil.immutableList( + new CodeStyleSelectSettingPresentation("DOWHILE_BRACE_FORCE", ApplicationBundle.message("wrapping.force.braces"), BRACE_VALUES, + BRACE_OPTIONS), + new CodeStyleSettingPresentation("WHILE_ON_NEW_LINE", ApplicationBundle.message("wrapping.while.on.new.line")) + )); + + result.put(new SettingsGroup(WRAPPING_SWITCH_STATEMENT), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("INDENT_CASE_FROM_SWITCH", ApplicationBundle.message("wrapping.indent.case.from.switch")), + new CodeStyleSettingPresentation("INDENT_BREAK_FROM_CASE", ApplicationBundle.message("wrapping.indent.break.from.case")) + )); + + putGroupTop(result, "RESOURCE_LIST_WRAP", WRAPPING_TRY_RESOURCE_LIST, WRAP_VALUES, WRAP_OPTIONS); + result.put(new SettingsGroup(WRAPPING_TRY_RESOURCE_LIST), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("ALIGN_MULTILINE_RESOURCES", ApplicationBundle.message("wrapping.align.when.multiline")), + new CodeStyleSettingPresentation("RESOURCE_LIST_LPAREN_ON_NEXT_LINE", + ApplicationBundle.message("wrapping.new.line.after.lpar")), + new CodeStyleSettingPresentation("RESOURCE_LIST_RPAREN_ON_NEXT_LINE", ApplicationBundle.message("wrapping.rpar.on.new.line")) + )); + + result.put(new SettingsGroup(WRAPPING_TRY_STATEMENT), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("CATCH_ON_NEW_LINE", ApplicationBundle.message("wrapping.catch.on.new.line")), + new CodeStyleSettingPresentation("FINALLY_ON_NEW_LINE", ApplicationBundle.message("wrapping.finally.on.new.line")) + )); + + putGroupTop(result, "BINARY_OPERATION_WRAP", WRAPPING_BINARY_OPERATION, WRAP_VALUES, WRAP_OPTIONS); + result.put(new SettingsGroup(WRAPPING_BINARY_OPERATION), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("ALIGN_MULTILINE_BINARY_OPERATION", + ApplicationBundle.message("wrapping.align.when.multiline")), + new CodeStyleSettingPresentation("BINARY_OPERATION_SIGN_ON_NEXT_LINE", + ApplicationBundle.message("wrapping.operation.sign.on.next.line")), + new CodeStyleSettingPresentation("ALIGN_MULTILINE_PARENTHESIZED_EXPRESSION", + ApplicationBundle.message("wrapping.align.parenthesised.when.multiline")), + new CodeStyleSettingPresentation("PARENTHESES_EXPRESSION_LPAREN_WRAP", + ApplicationBundle.message("wrapping.new.line.after.lpar")), + new CodeStyleSettingPresentation("PARENTHESES_EXPRESSION_RPAREN_WRAP", ApplicationBundle.message("wrapping.rpar.on.new.line")) + )); + + putGroupTop(result, "ASSIGNMENT_WRAP", WRAPPING_ASSIGNMENT, WRAP_VALUES, WRAP_OPTIONS); + result.put(new SettingsGroup(WRAPPING_ASSIGNMENT), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("ALIGN_MULTILINE_ASSIGNMENT", ApplicationBundle.message("wrapping.align.when.multiline")), + new CodeStyleSettingPresentation("PLACE_ASSIGNMENT_SIGN_ON_NEXT_LINE", + ApplicationBundle.message("wrapping.assignment.sign.on.next.line")) + )); + + result.put(new SettingsGroup(WRAPPING_FIELDS_VARIABLES_GROUPS), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("ALIGN_GROUP_FIELD_DECLARATIONS", + ApplicationBundle.message("wrapping.align.fields.in.columns")), + new CodeStyleSettingPresentation("ALIGN_CONSECUTIVE_VARIABLE_DECLARATIONS", + ApplicationBundle.message("wrapping.align.variables.in.columns")) + )); + + putGroupTop(result, "TERNARY_OPERATION_WRAP", WRAPPING_TERNARY_OPERATION, WRAP_VALUES, WRAP_OPTIONS); + result.put(new SettingsGroup(WRAPPING_TERNARY_OPERATION), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("ALIGN_MULTILINE_TERNARY_OPERATION", + ApplicationBundle.message("wrapping.align.when.multiline")), + new CodeStyleSettingPresentation("TERNARY_OPERATION_SIGNS_ON_NEXT_LINE", + ApplicationBundle.message("wrapping.quest.and.colon.signs.on.next.line")) + )); + + putGroupTop(result, "ARRAY_INITIALIZER_WRAP", WRAPPING_ARRAY_INITIALIZER, WRAP_VALUES, WRAP_OPTIONS); + result.put(new SettingsGroup(WRAPPING_ARRAY_INITIALIZER), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION", + ApplicationBundle.message("wrapping.align.when.multiline")), + new CodeStyleSettingPresentation("ARRAY_INITIALIZER_LBRACE_ON_NEXT_LINE", + ApplicationBundle.message("wrapping.new.line.after.lbrace")), + new CodeStyleSettingPresentation("ARRAY_INITIALIZER_RBRACE_ON_NEXT_LINE", + ApplicationBundle.message("wrapping.rbrace.on.new.line")) + )); + + result.put(new SettingsGroup(WRAPPING_MODIFIER_LIST), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("MODIFIER_LIST_WRAP", ApplicationBundle.message("wrapping.after.modifier.list")) + )); + + putGroupTop(result, "ASSERT_STATEMENT_WRAP", WRAPPING_ASSERT_STATEMENT, WRAP_VALUES, WRAP_OPTIONS); + result.put(new SettingsGroup(WRAPPING_ASSERT_STATEMENT), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("ASSERT_STATEMENT_COLON_ON_NEXT_LINE", + ApplicationBundle.message("wrapping.colon.signs.on.next.line")) + )); + + putGroupTop(result, "ENUM_CONSTANTS_WRAP", ApplicationBundle.message("wrapping.enum.constants"), WRAP_VALUES, WRAP_OPTIONS); + putGroupTop(result, "CLASS_ANNOTATION_WRAP", ApplicationBundle.message("wrapping.classes.annotation"), WRAP_VALUES, WRAP_OPTIONS); + putGroupTop(result, "METHOD_ANNOTATION_WRAP", ApplicationBundle.message("wrapping.methods.annotation"), WRAP_VALUES, WRAP_OPTIONS); + putGroupTop(result, "FIELD_ANNOTATION_WRAP", ApplicationBundle.message("wrapping.fields.annotation"), WRAP_VALUES, WRAP_OPTIONS); + putGroupTop(result, "PARAMETER_ANNOTATION_WRAP", ApplicationBundle.message("wrapping.parameters.annotation"), WRAP_VALUES, WRAP_OPTIONS); + putGroupTop(result, "VARIABLE_ANNOTATION_WRAP", ApplicationBundle.message("wrapping.local.variables.annotation"), WRAP_VALUES, + WRAP_OPTIONS); + WRAPPING_AND_BRACES_STANDARD_SETTINGS = Collections.unmodifiableMap(result); + + //-----------------------------------INDENT_SETTINGS----------------------------------------------------- + + result = ContainerUtil.newLinkedHashMap(); + result.put(new SettingsGroup(null), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("INDENT_SIZE", ApplicationBundle.message("editbox.indent.indent")) + )); + result.put(new SettingsGroup(null), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("CONTINUATION_INDENT_SIZE", ApplicationBundle.message("editbox.indent.continuation.indent")) + )); + result.put(new SettingsGroup(null), ContainerUtil.immutableList( + new CodeStyleSettingPresentation("TAB_SIZE", ApplicationBundle.message("editbox.indent.tab.size")) + )); + INDENT_STANDARD_SETTINGS = Collections.unmodifiableMap(result); + } + + /** + * Returns an immutable map containing all standard settings in a mapping of type (group -> settings contained in the group). + * Notice that lists containing settings for a specific group are also immutable. Use copies to make modifications. + * @param settingsType type to get standard settings for + * @return mapping setting groups to contained setting presentations + */ + @NotNull + public static Map> getStandardSettings(LanguageCodeStyleSettingsProvider.SettingsType settingsType) { + switch (settingsType) { + case BLANK_LINES_SETTINGS: + return BLANK_LINES_STANDARD_SETTINGS; + case SPACING_SETTINGS: + return SPACING_STANDARD_SETTINGS; + case WRAPPING_AND_BRACES_SETTINGS: + return WRAPPING_AND_BRACES_STANDARD_SETTINGS; + case INDENT_SETTINGS: + return INDENT_STANDARD_SETTINGS; + case LANGUAGE_SPECIFIC: + } + return ContainerUtil.newLinkedHashMap(); + } +} diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/CodeStyleBlankLinesPanel.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/CodeStyleBlankLinesPanel.java index 761b7176ba04..24efd7323227 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/CodeStyleBlankLinesPanel.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/CodeStyleBlankLinesPanel.java @@ -18,16 +18,15 @@ package com.intellij.application.options.codeStyle; import com.intellij.openapi.application.ApplicationBundle; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Trinity; -import com.intellij.psi.codeStyle.CodeStyleSettings; -import com.intellij.psi.codeStyle.CommonCodeStyleSettings; -import com.intellij.psi.codeStyle.CustomCodeStyleSettings; -import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider; +import com.intellij.psi.codeStyle.*; +import com.intellij.psi.codeStyle.presentation.CodeStyleSettingPresentation; import com.intellij.ui.OptionGroup; import com.intellij.ui.ScrollPaneFactory; import com.intellij.ui.components.JBLabel; import com.intellij.util.containers.MultiMap; import com.intellij.util.ui.UIUtil; import gnu.trove.THashMap; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -62,8 +61,11 @@ public class CodeStyleBlankLinesPanel extends CustomizableLanguageCodeStylePanel JPanel optionsPanel = new JPanel(new GridBagLayout()); - OptionGroup keepBlankLinesOptionsGroup = createKeepBlankLinesOptionsGroup(); - OptionGroup blankLinesOptionsGroup = createBlankLinesOptionsGroup(); + Map> settings = CodeStyleSettingPresentation + .getStandardSettings(getSettingsType()); + + OptionGroup keepBlankLinesOptionsGroup = createOptionsGroup(BLANK_LINES_KEEP, settings.get(new CodeStyleSettingPresentation.SettingsGroup(BLANK_LINES_KEEP))); + OptionGroup blankLinesOptionsGroup = createOptionsGroup(BLANK_LINES, settings.get(new CodeStyleSettingPresentation.SettingsGroup(BLANK_LINES))); if (keepBlankLinesOptionsGroup != null) { keepBlankLinesOptionsGroup.setAnchor(keepBlankLinesOptionsGroup.findAnchor()); optionsPanel.add(keepBlankLinesOptionsGroup.createPanel(), @@ -109,37 +111,13 @@ public class CodeStyleBlankLinesPanel extends CustomizableLanguageCodeStylePanel } @Nullable - private OptionGroup createBlankLinesOptionsGroup() { - OptionGroup optionGroup = new OptionGroup(BLANK_LINES); + private OptionGroup createOptionsGroup(@NotNull String groupName, @NotNull List settings) { + OptionGroup optionGroup = new OptionGroup(groupName); - createOption(optionGroup, ApplicationBundle.message("editbox.blanklines.before.package.statement"), "BLANK_LINES_BEFORE_PACKAGE"); - createOption(optionGroup, ApplicationBundle.message("editbox.blanklines.after.package.statement"), "BLANK_LINES_AFTER_PACKAGE"); - createOption(optionGroup, ApplicationBundle.message("editbox.blanklines.before.imports"), "BLANK_LINES_BEFORE_IMPORTS"); - createOption(optionGroup, ApplicationBundle.message("editbox.blanklines.after.imports"), "BLANK_LINES_AFTER_IMPORTS"); - createOption(optionGroup, ApplicationBundle.message("editbox.blanklines.around.class"), "BLANK_LINES_AROUND_CLASS"); - createOption(optionGroup, ApplicationBundle.message("editbox.blanklines.after.class.header"), "BLANK_LINES_AFTER_CLASS_HEADER"); - createOption(optionGroup, ApplicationBundle.message("editbox.blanklines.after.anonymous.class.header"), - "BLANK_LINES_AFTER_ANONYMOUS_CLASS_HEADER"); - createOption(optionGroup, "Around field in interface:", "BLANK_LINES_AROUND_FIELD_IN_INTERFACE"); - createOption(optionGroup, ApplicationBundle.message("editbox.blanklines.around.field"), "BLANK_LINES_AROUND_FIELD"); - createOption(optionGroup, "Around method in interface:", "BLANK_LINES_AROUND_METHOD_IN_INTERFACE"); - createOption(optionGroup, ApplicationBundle.message("editbox.blanklines.around.method"), "BLANK_LINES_AROUND_METHOD"); - createOption(optionGroup, ApplicationBundle.message("editbox.blanklines.before.method.body"), "BLANK_LINES_BEFORE_METHOD_BODY"); - initCustomOptions(optionGroup, BLANK_LINES); - - if (optionGroup.getComponents().length == 0) return null; - - return optionGroup; - } - - @Nullable - private OptionGroup createKeepBlankLinesOptionsGroup() { - OptionGroup optionGroup = new OptionGroup(BLANK_LINES_KEEP); - - createOption(optionGroup, ApplicationBundle.message("editbox.keep.blanklines.in.declarations"), "KEEP_BLANK_LINES_IN_DECLARATIONS"); - createOption(optionGroup, ApplicationBundle.message("editbox.keep.blanklines.in.code"), "KEEP_BLANK_LINES_IN_CODE"); - createOption(optionGroup, ApplicationBundle.message("editbox.keep.blanklines.before.rbrace"), "KEEP_BLANK_LINES_BEFORE_RBRACE"); - initCustomOptions(optionGroup, BLANK_LINES_KEEP); + for (CodeStyleSettingPresentation setting: settings) { + createOption(optionGroup, setting.getUiName(), setting.getFieldName()); + } + initCustomOptions(optionGroup, groupName); if (optionGroup.getComponents().length == 0) return null; diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/CodeStyleSpacesPanel.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/CodeStyleSpacesPanel.java index be4ef0ddd131..1f9f69370df8 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/CodeStyleSpacesPanel.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/CodeStyleSpacesPanel.java @@ -16,9 +16,13 @@ package com.intellij.application.options.codeStyle; import com.intellij.openapi.application.ApplicationBundle; +import com.intellij.psi.codeStyle.presentation.CodeStyleSettingPresentation; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider; +import java.util.List; +import java.util.Map; + public class CodeStyleSpacesPanel extends OptionTreeWithPreviewPanel { public CodeStyleSpacesPanel(CodeStyleSettings settings) { super(settings); @@ -32,100 +36,16 @@ public class CodeStyleSpacesPanel extends OptionTreeWithPreviewPanel { @Override protected void initTables() { - initBooleanField("SPACE_BEFORE_METHOD_PARENTHESES", ApplicationBundle.message("checkbox.spaces.method.declaration.parentheses"), SPACES_BEFORE_PARENTHESES); - initBooleanField("SPACE_BEFORE_METHOD_CALL_PARENTHESES", ApplicationBundle.message("checkbox.spaces.method.call.parentheses"), SPACES_BEFORE_PARENTHESES); - initBooleanField("SPACE_BEFORE_IF_PARENTHESES", ApplicationBundle.message("checkbox.spaces.if.parentheses"), SPACES_BEFORE_PARENTHESES); - initBooleanField("SPACE_BEFORE_FOR_PARENTHESES", ApplicationBundle.message("checkbox.spaces.for.parentheses"), SPACES_BEFORE_PARENTHESES); - initBooleanField("SPACE_BEFORE_WHILE_PARENTHESES", ApplicationBundle.message("checkbox.spaces.while.parentheses"), SPACES_BEFORE_PARENTHESES); - initBooleanField("SPACE_BEFORE_SWITCH_PARENTHESES", ApplicationBundle.message("checkbox.spaces.switch.parentheses"), SPACES_BEFORE_PARENTHESES); - initBooleanField("SPACE_BEFORE_TRY_PARENTHESES", ApplicationBundle.message("checkbox.spaces.try.parentheses"), SPACES_BEFORE_PARENTHESES); - initBooleanField("SPACE_BEFORE_CATCH_PARENTHESES", ApplicationBundle.message("checkbox.spaces.catch.parentheses"), SPACES_BEFORE_PARENTHESES); - initBooleanField("SPACE_BEFORE_SYNCHRONIZED_PARENTHESES", ApplicationBundle.message("checkbox.spaces.synchronized.parentheses"), SPACES_BEFORE_PARENTHESES); - initBooleanField("SPACE_BEFORE_ANOTATION_PARAMETER_LIST", ApplicationBundle.message("checkbox.spaces.annotation.parameters"), SPACES_BEFORE_PARENTHESES); - initCustomOptions(SPACES_BEFORE_PARENTHESES); + Map> settingsMap = CodeStyleSettingPresentation + .getStandardSettings(getSettingsType()); - initBooleanField("SPACE_AROUND_ASSIGNMENT_OPERATORS", ApplicationBundle.message("checkbox.spaces.assignment.operators"), SPACES_AROUND_OPERATORS); - initBooleanField("SPACE_AROUND_LOGICAL_OPERATORS", ApplicationBundle.message("checkbox.spaces.logical.operators"), SPACES_AROUND_OPERATORS); - initBooleanField("SPACE_AROUND_EQUALITY_OPERATORS", ApplicationBundle.message("checkbox.spaces.equality.operators"), SPACES_AROUND_OPERATORS); - initBooleanField("SPACE_AROUND_RELATIONAL_OPERATORS", ApplicationBundle.message("checkbox.spaces.relational.operators"), SPACES_AROUND_OPERATORS); - initBooleanField("SPACE_AROUND_BITWISE_OPERATORS", ApplicationBundle.message("checkbox.spaces.bitwise.operators"), SPACES_AROUND_OPERATORS); - initBooleanField("SPACE_AROUND_ADDITIVE_OPERATORS", ApplicationBundle.message("checkbox.spaces.additive.operators"), SPACES_AROUND_OPERATORS); - initBooleanField("SPACE_AROUND_MULTIPLICATIVE_OPERATORS", ApplicationBundle.message("checkbox.spaces.multiplicative.operators"), SPACES_AROUND_OPERATORS); - initBooleanField("SPACE_AROUND_SHIFT_OPERATORS", ApplicationBundle.message("checkbox.spaces.shift.operators"), SPACES_AROUND_OPERATORS); - initBooleanField("SPACE_AROUND_UNARY_OPERATOR", ApplicationBundle.message("checkbox.spaces.around.unary.operator"), SPACES_AROUND_OPERATORS); - initBooleanField("SPACE_AROUND_LAMBDA_ARROW", ApplicationBundle.message("checkbox.spaces.around.lambda.arrow"), SPACES_AROUND_OPERATORS); - initBooleanField("SPACE_AROUND_METHOD_REF_DBL_COLON", ApplicationBundle.message("checkbox.spaces.around.method.ref.dbl.colon.arrow"), SPACES_AROUND_OPERATORS); - initCustomOptions(SPACES_AROUND_OPERATORS); - - initBooleanField("SPACE_BEFORE_CLASS_LBRACE", ApplicationBundle.message("checkbox.spaces.class.left.brace"), SPACES_BEFORE_LEFT_BRACE); - initBooleanField("SPACE_BEFORE_METHOD_LBRACE", ApplicationBundle.message("checkbox.spaces.method.left.brace"), SPACES_BEFORE_LEFT_BRACE); - initBooleanField("SPACE_BEFORE_IF_LBRACE", ApplicationBundle.message("checkbox.spaces.if.left.brace"), SPACES_BEFORE_LEFT_BRACE); - initBooleanField("SPACE_BEFORE_ELSE_LBRACE", ApplicationBundle.message("checkbox.spaces.else.left.brace"), SPACES_BEFORE_LEFT_BRACE); - initBooleanField("SPACE_BEFORE_FOR_LBRACE", ApplicationBundle.message("checkbox.spaces.for.left.brace"), SPACES_BEFORE_LEFT_BRACE); - initBooleanField("SPACE_BEFORE_WHILE_LBRACE", ApplicationBundle.message("checkbox.spaces.while.left.brace"), SPACES_BEFORE_LEFT_BRACE); - initBooleanField("SPACE_BEFORE_DO_LBRACE", ApplicationBundle.message("checkbox.spaces.do.left.brace"), SPACES_BEFORE_LEFT_BRACE); - initBooleanField("SPACE_BEFORE_SWITCH_LBRACE", ApplicationBundle.message("checkbox.spaces.switch.left.brace"), SPACES_BEFORE_LEFT_BRACE); - initBooleanField("SPACE_BEFORE_TRY_LBRACE", ApplicationBundle.message("checkbox.spaces.try.left.brace"), SPACES_BEFORE_LEFT_BRACE); - initBooleanField("SPACE_BEFORE_CATCH_LBRACE", ApplicationBundle.message("checkbox.spaces.catch.left.brace"), SPACES_BEFORE_LEFT_BRACE); - initBooleanField("SPACE_BEFORE_FINALLY_LBRACE", ApplicationBundle.message("checkbox.spaces.finally.left.brace"), SPACES_BEFORE_LEFT_BRACE); - initBooleanField("SPACE_BEFORE_SYNCHRONIZED_LBRACE", ApplicationBundle.message("checkbox.spaces.synchronized.left.brace"), SPACES_BEFORE_LEFT_BRACE); - initBooleanField("SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE", ApplicationBundle.message("checkbox.spaces.array.initializer.left.brace"), SPACES_BEFORE_LEFT_BRACE); - initBooleanField("SPACE_BEFORE_ANNOTATION_ARRAY_INITIALIZER_LBRACE", ApplicationBundle.message("checkbox.spaces.annotation.array.initializer.left.brace"), SPACES_BEFORE_LEFT_BRACE); - initCustomOptions(SPACES_BEFORE_LEFT_BRACE); - - initBooleanField("SPACE_BEFORE_ELSE_KEYWORD", ApplicationBundle.message("checkbox.spaces.else.keyword"), SPACES_BEFORE_KEYWORD); - initBooleanField("SPACE_BEFORE_WHILE_KEYWORD", ApplicationBundle.message("checkbox.spaces.while.keyword"), SPACES_BEFORE_KEYWORD); - initBooleanField("SPACE_BEFORE_CATCH_KEYWORD", ApplicationBundle.message("checkbox.spaces.catch.keyword"), SPACES_BEFORE_KEYWORD); - initBooleanField("SPACE_BEFORE_FINALLY_KEYWORD", ApplicationBundle.message("checkbox.spaces.finally.keyword"), SPACES_BEFORE_KEYWORD); - initCustomOptions(SPACES_BEFORE_KEYWORD); - - initBooleanField("SPACE_WITHIN_BRACES", ApplicationBundle.message("checkbox.spaces.within.braces"), SPACES_WITHIN); - initBooleanField("SPACE_WITHIN_BRACKETS", ApplicationBundle.message("checkbox.spaces.within.brackets"), SPACES_WITHIN); - initBooleanField("SPACE_WITHIN_ARRAY_INITIALIZER_BRACES", ApplicationBundle.message("checkbox.spaces.within.array.initializer.braces"), SPACES_WITHIN); - initBooleanField("SPACE_WITHIN_EMPTY_ARRAY_INITIALIZER_BRACES", ApplicationBundle.message("checkbox.spaces.within.empty.array.initializer.braces"), SPACES_WITHIN); - - initBooleanField("SPACE_WITHIN_PARENTHESES", ApplicationBundle.message("checkbox.spaces.within.parentheses"), SPACES_WITHIN); - initBooleanField("SPACE_WITHIN_METHOD_CALL_PARENTHESES", ApplicationBundle.message("checkbox.spaces.checkbox.spaces.method.call.parentheses"), SPACES_WITHIN); - initBooleanField( - "SPACE_WITHIN_EMPTY_METHOD_CALL_PARENTHESES", - ApplicationBundle.message("checkbox.spaces.checkbox.spaces.empty.method.call.parentheses"), - SPACES_WITHIN - ); - initBooleanField("SPACE_WITHIN_METHOD_PARENTHESES", ApplicationBundle.message("checkbox.spaces.checkbox.spaces.method.declaration.parentheses"), SPACES_WITHIN); - initBooleanField( - "SPACE_WITHIN_EMPTY_METHOD_PARENTHESES", - ApplicationBundle.message("checkbox.spaces.checkbox.spaces.empty.method.declaration.parentheses"), - SPACES_WITHIN - ); - initBooleanField("SPACE_WITHIN_IF_PARENTHESES", ApplicationBundle.message("checkbox.spaces.if.parentheses"), SPACES_WITHIN); - initBooleanField("SPACE_WITHIN_FOR_PARENTHESES", ApplicationBundle.message("checkbox.spaces.for.parentheses"), SPACES_WITHIN); - initBooleanField("SPACE_WITHIN_WHILE_PARENTHESES", ApplicationBundle.message("checkbox.spaces.while.parentheses"), SPACES_WITHIN); - initBooleanField("SPACE_WITHIN_SWITCH_PARENTHESES", ApplicationBundle.message("checkbox.spaces.switch.parentheses"), SPACES_WITHIN); - initBooleanField("SPACE_WITHIN_TRY_PARENTHESES", ApplicationBundle.message("checkbox.spaces.try.parentheses"), SPACES_WITHIN); - initBooleanField("SPACE_WITHIN_CATCH_PARENTHESES", ApplicationBundle.message("checkbox.spaces.catch.parentheses"), SPACES_WITHIN); - initBooleanField("SPACE_WITHIN_SYNCHRONIZED_PARENTHESES", ApplicationBundle.message("checkbox.spaces.synchronized.parentheses"), SPACES_WITHIN); - initBooleanField("SPACE_WITHIN_CAST_PARENTHESES", ApplicationBundle.message("checkbox.spaces.type.cast.parentheses"), SPACES_WITHIN); - initBooleanField("SPACE_WITHIN_ANNOTATION_PARENTHESES", ApplicationBundle.message("checkbox.spaces.annotation.parentheses"), SPACES_WITHIN); - initCustomOptions(SPACES_WITHIN); - - initBooleanField("SPACE_BEFORE_QUEST", ApplicationBundle.message("checkbox.spaces.before.question"), SPACES_IN_TERNARY_OPERATOR); - initBooleanField("SPACE_AFTER_QUEST", ApplicationBundle.message("checkbox.spaces.after.question"), SPACES_IN_TERNARY_OPERATOR); - initBooleanField("SPACE_BEFORE_COLON", ApplicationBundle.message("checkbox.spaces.before.colon"), SPACES_IN_TERNARY_OPERATOR); - initBooleanField("SPACE_AFTER_COLON", ApplicationBundle.message("checkbox.spaces.after.colon"), SPACES_IN_TERNARY_OPERATOR); - initCustomOptions(SPACES_IN_TERNARY_OPERATOR); - - initBooleanField("SPACE_AFTER_COMMA_IN_TYPE_ARGUMENTS", ApplicationBundle.message("checkbox.spaces.after.comma"), SPACES_WITHIN_TYPE_ARGUMENTS); - initCustomOptions(SPACES_WITHIN_TYPE_ARGUMENTS); - - initCustomOptions(SPACES_IN_TYPE_ARGUMENTS); - initCustomOptions(SPACES_IN_TYPE_PARAMETERS); - - initBooleanField("SPACE_BEFORE_COMMA", ApplicationBundle.message("checkbox.spaces.before.comma"), SPACES_OTHER); - initBooleanField("SPACE_AFTER_COMMA", ApplicationBundle.message("checkbox.spaces.after.comma"), SPACES_OTHER); - initBooleanField("SPACE_BEFORE_SEMICOLON", ApplicationBundle.message("checkbox.spaces.before.semicolon"), SPACES_OTHER); - initBooleanField("SPACE_AFTER_SEMICOLON", ApplicationBundle.message("checkbox.spaces.after.semicolon"), SPACES_OTHER); - initBooleanField("SPACE_AFTER_TYPE_CAST", ApplicationBundle.message("checkbox.spaces.after.type.cast"), SPACES_OTHER); - initCustomOptions(SPACES_OTHER); + for (Map.Entry> entry: settingsMap.entrySet()) { + String groupName = entry.getKey().name; + for (CodeStyleSettingPresentation setting: entry.getValue()) { + initBooleanField(setting.getFieldName(), setting.getUiName(), groupName); + } + initCustomOptions(groupName); + } } @Override diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/OptionTableWithPreviewPanel.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/OptionTableWithPreviewPanel.java index 6f8e536daba2..7c64542ed8b6 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/OptionTableWithPreviewPanel.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/OptionTableWithPreviewPanel.java @@ -16,9 +16,7 @@ package com.intellij.application.options.codeStyle; import com.intellij.openapi.application.ApplicationBundle; -import com.intellij.psi.codeStyle.CodeStyleSettings; -import com.intellij.psi.codeStyle.CommonCodeStyleSettings; -import com.intellij.psi.codeStyle.CustomCodeStyleSettings; +import com.intellij.psi.codeStyle.*; import com.intellij.ui.SpeedSearchComparator; import com.intellij.ui.TreeTableSpeedSearch; import com.intellij.ui.components.JBCheckBox; @@ -283,7 +281,7 @@ public abstract class OptionTableWithPreviewPanel extends CustomizableLanguageCo return result == null ? defaultName : result; } - private static void expandTree(final JTree tree) { + public static void expandTree(final JTree tree) { int oldRowCount = 0; do { int rowCount = tree.getRowCount(); diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/WrappingAndBracesPanel.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/WrappingAndBracesPanel.java index 66b398a83250..d5e802db1ffa 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/WrappingAndBracesPanel.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/WrappingAndBracesPanel.java @@ -16,14 +16,17 @@ package com.intellij.application.options.codeStyle; import com.intellij.openapi.application.ApplicationBundle; -import com.intellij.psi.codeStyle.CodeStyleSettings; -import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider; +import com.intellij.psi.codeStyle.*; +import com.intellij.psi.codeStyle.presentation.CodeStyleBoundedIntegerSettingPresentation; +import com.intellij.psi.codeStyle.presentation.CodeStyleSelectSettingPresentation; +import com.intellij.psi.codeStyle.presentation.CodeStyleSettingPresentation; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; +import java.util.List; import java.util.Map; import java.util.Set; @@ -60,110 +63,26 @@ public class WrappingAndBracesPanel extends OptionTableWithPreviewPanel { @Override protected void initTables() { - addOption("RIGHT_MARGIN", ApplicationBundle.message("editbox.right.margin.columns"), null, 0, 999, -1, ApplicationBundle.message("settings.code.style.default.general")); - addOption("WRAP_ON_TYPING", ApplicationBundle.message("wrapping.wrap.on.typing"), null, WRAP_ON_TYPING_OPTIONS, WRAP_ON_TYPING_VALUES); - - addOption("KEEP_LINE_BREAKS", ApplicationBundle.message("wrapping.keep.line.breaks"), WRAPPING_KEEP); - addOption("KEEP_FIRST_COLUMN_COMMENT", ApplicationBundle.message("wrapping.keep.comment.at.first.column"), WRAPPING_KEEP); - addOption("KEEP_CONTROL_STATEMENT_IN_ONE_LINE", ApplicationBundle.message("checkbox.keep.when.reformatting.control.statement.in.one.line"), WRAPPING_KEEP); - addOption("KEEP_MULTIPLE_EXPRESSIONS_IN_ONE_LINE", ApplicationBundle.message("wrapping.keep.multiple.expressions.in.one.line"), WRAPPING_KEEP); - addOption("KEEP_SIMPLE_BLOCKS_IN_ONE_LINE", ApplicationBundle.message("wrapping.keep.simple.blocks.in.one.line"), WRAPPING_KEEP); - addOption("KEEP_SIMPLE_METHODS_IN_ONE_LINE", ApplicationBundle.message("wrapping.keep.simple.methods.in.one.line"), WRAPPING_KEEP); - addOption("KEEP_SIMPLE_CLASSES_IN_ONE_LINE", ApplicationBundle.message("wrapping.keep.simple.classes.in.one.line"), WRAPPING_KEEP); - - addOption("WRAP_LONG_LINES", ApplicationBundle.message("wrapping.long.lines"), null); - addOption("WRAP_COMMENTS", ApplicationBundle.message("wrapping.comments.wrap.at.right.margin"), WRAPPING_COMMENTS); - - addOption("CLASS_BRACE_STYLE", ApplicationBundle.message("wrapping.brace.placement.class.declaration"), WRAPPING_BRACES, BRACE_PLACEMENT_OPTIONS, BRACE_PLACEMENT_VALUES); - addOption("METHOD_BRACE_STYLE", ApplicationBundle.message("wrapping.brace.placement.method.declaration"), WRAPPING_BRACES, BRACE_PLACEMENT_OPTIONS, BRACE_PLACEMENT_VALUES); - addOption("BRACE_STYLE", ApplicationBundle.message("wrapping.brace.placement.other"), WRAPPING_BRACES, BRACE_PLACEMENT_OPTIONS, BRACE_PLACEMENT_VALUES); - - addOption("EXTENDS_LIST_WRAP", WRAPPING_EXTENDS_LIST, WRAP_OPTIONS, WRAP_VALUES); - addOption("ALIGN_MULTILINE_EXTENDS_LIST", ApplicationBundle.message("wrapping.align.when.multiline"), WRAPPING_EXTENDS_LIST); - - addOption("EXTENDS_KEYWORD_WRAP", WRAPPING_EXTENDS_KEYWORD, WRAP_OPTIONS_FOR_SINGLETON, WRAP_VALUES_FOR_SINGLETON); - - addOption("THROWS_LIST_WRAP", WRAPPING_THROWS_LIST, WRAP_OPTIONS, WRAP_VALUES); - addOption("ALIGN_MULTILINE_THROWS_LIST", ApplicationBundle.message("wrapping.align.when.multiline"), WRAPPING_THROWS_LIST); - addOption("ALIGN_THROWS_KEYWORD", ApplicationBundle.message("wrapping.align.throws.keyword"), WRAPPING_THROWS_LIST); - addOption("THROWS_KEYWORD_WRAP", WRAPPING_THROWS_KEYWORD, WRAP_OPTIONS_FOR_SINGLETON, WRAP_VALUES_FOR_SINGLETON); - - addOption("METHOD_PARAMETERS_WRAP", WRAPPING_METHOD_PARAMETERS, WRAP_OPTIONS, WRAP_VALUES); - addOption("ALIGN_MULTILINE_PARAMETERS", ApplicationBundle.message("wrapping.align.when.multiline"), WRAPPING_METHOD_PARAMETERS); - addOption("METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE", ApplicationBundle.message("wrapping.new.line.after.lpar"), WRAPPING_METHOD_PARAMETERS); - addOption("METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE", ApplicationBundle.message("wrapping.rpar.on.new.line"), WRAPPING_METHOD_PARAMETERS); - - addOption("CALL_PARAMETERS_WRAP", WRAPPING_METHOD_ARGUMENTS_WRAPPING, WRAP_OPTIONS, WRAP_VALUES); - addOption("ALIGN_MULTILINE_PARAMETERS_IN_CALLS", ApplicationBundle.message("wrapping.align.when.multiline"), WRAPPING_METHOD_ARGUMENTS_WRAPPING); - addOption("PREFER_PARAMETERS_WRAP", ApplicationBundle.message("wrapping.take.priority.over.call.chain.wrapping"), WRAPPING_METHOD_ARGUMENTS_WRAPPING); - addOption("CALL_PARAMETERS_LPAREN_ON_NEXT_LINE", ApplicationBundle.message("wrapping.new.line.after.lpar"), WRAPPING_METHOD_ARGUMENTS_WRAPPING); - addOption("CALL_PARAMETERS_RPAREN_ON_NEXT_LINE", ApplicationBundle.message("wrapping.rpar.on.new.line"), WRAPPING_METHOD_ARGUMENTS_WRAPPING); - - addOption("ALIGN_MULTILINE_METHOD_BRACKETS", ApplicationBundle.message("wrapping.align.when.multiline"), WRAPPING_METHOD_PARENTHESES); - - addOption("METHOD_CALL_CHAIN_WRAP", WRAPPING_CALL_CHAIN, WRAP_OPTIONS, WRAP_VALUES); - addOption("WRAP_FIRST_METHOD_IN_CALL_CHAIN", ApplicationBundle.message("wrapping.chained.method.call.first.on.new.line"), WRAPPING_CALL_CHAIN); - addOption("ALIGN_MULTILINE_CHAINED_METHODS", ApplicationBundle.message("wrapping.align.when.multiline"), WRAPPING_CALL_CHAIN); - - addOption("IF_BRACE_FORCE", ApplicationBundle.message("wrapping.force.braces"), WRAPPING_IF_STATEMENT, BRACE_OPTIONS, BRACE_VALUES); - addOption("ELSE_ON_NEW_LINE", ApplicationBundle.message("wrapping.else.on.new.line"), WRAPPING_IF_STATEMENT); - addOption("SPECIAL_ELSE_IF_TREATMENT", ApplicationBundle.message("wrapping.special.else.if.braces.treatment"), WRAPPING_IF_STATEMENT); - - addOption("FOR_STATEMENT_WRAP", WRAPPING_FOR_STATEMENT, WRAP_OPTIONS, WRAP_VALUES); - addOption("ALIGN_MULTILINE_FOR", ApplicationBundle.message("wrapping.align.when.multiline"), WRAPPING_FOR_STATEMENT); - addOption("FOR_STATEMENT_LPAREN_ON_NEXT_LINE", ApplicationBundle.message("wrapping.new.line.after.lpar"), WRAPPING_FOR_STATEMENT); - addOption("FOR_STATEMENT_RPAREN_ON_NEXT_LINE", ApplicationBundle.message("wrapping.rpar.on.new.line"), WRAPPING_FOR_STATEMENT); - addOption("FOR_BRACE_FORCE", ApplicationBundle.message("wrapping.force.braces"), WRAPPING_FOR_STATEMENT, BRACE_OPTIONS, BRACE_VALUES); - - addOption("WHILE_BRACE_FORCE", ApplicationBundle.message("wrapping.force.braces"), WRAPPING_WHILE_STATEMENT, BRACE_OPTIONS, BRACE_VALUES); - addOption("DOWHILE_BRACE_FORCE", ApplicationBundle.message("wrapping.force.braces"), WRAPPING_DOWHILE_STATEMENT, BRACE_OPTIONS, BRACE_VALUES); - addOption("WHILE_ON_NEW_LINE", ApplicationBundle.message("wrapping.while.on.new.line"), WRAPPING_DOWHILE_STATEMENT); - - addOption("INDENT_CASE_FROM_SWITCH", ApplicationBundle.message("wrapping.indent.case.from.switch"), WRAPPING_SWITCH_STATEMENT); - addOption("INDENT_BREAK_FROM_CASE", ApplicationBundle.message("wrapping.indent.break.from.case"), WRAPPING_SWITCH_STATEMENT); - - addOption("RESOURCE_LIST_WRAP", WRAPPING_TRY_RESOURCE_LIST, WRAP_OPTIONS, WRAP_VALUES); - addOption("ALIGN_MULTILINE_RESOURCES", ApplicationBundle.message("wrapping.align.when.multiline"), WRAPPING_TRY_RESOURCE_LIST); - addOption("RESOURCE_LIST_LPAREN_ON_NEXT_LINE", ApplicationBundle.message("wrapping.new.line.after.lpar"), WRAPPING_TRY_RESOURCE_LIST); - addOption("RESOURCE_LIST_RPAREN_ON_NEXT_LINE", ApplicationBundle.message("wrapping.rpar.on.new.line"), WRAPPING_TRY_RESOURCE_LIST); - - addOption("CATCH_ON_NEW_LINE", ApplicationBundle.message("wrapping.catch.on.new.line"), WRAPPING_TRY_STATEMENT); - addOption("FINALLY_ON_NEW_LINE", ApplicationBundle.message("wrapping.finally.on.new.line"), WRAPPING_TRY_STATEMENT); - - addOption("BINARY_OPERATION_WRAP", WRAPPING_BINARY_OPERATION, WRAP_OPTIONS, WRAP_VALUES); - addOption("ALIGN_MULTILINE_BINARY_OPERATION", ApplicationBundle.message("wrapping.align.when.multiline"), WRAPPING_BINARY_OPERATION); - addOption("BINARY_OPERATION_SIGN_ON_NEXT_LINE", ApplicationBundle.message("wrapping.operation.sign.on.next.line"), WRAPPING_BINARY_OPERATION); - addOption("ALIGN_MULTILINE_PARENTHESIZED_EXPRESSION", ApplicationBundle.message("wrapping.align.parenthesised.when.multiline"), WRAPPING_BINARY_OPERATION); - addOption("PARENTHESES_EXPRESSION_LPAREN_WRAP", ApplicationBundle.message("wrapping.new.line.after.lpar"), WRAPPING_BINARY_OPERATION); - addOption("PARENTHESES_EXPRESSION_RPAREN_WRAP", ApplicationBundle.message("wrapping.rpar.on.new.line"), WRAPPING_BINARY_OPERATION); - - addOption("ASSIGNMENT_WRAP", WRAPPING_ASSIGNMENT, WRAP_OPTIONS, WRAP_VALUES); - addOption("ALIGN_MULTILINE_ASSIGNMENT", ApplicationBundle.message("wrapping.align.when.multiline"), WRAPPING_ASSIGNMENT); - addOption("PLACE_ASSIGNMENT_SIGN_ON_NEXT_LINE", ApplicationBundle.message("wrapping.assignment.sign.on.next.line"), WRAPPING_ASSIGNMENT); - - addOption("ALIGN_GROUP_FIELD_DECLARATIONS", ApplicationBundle.message("wrapping.align.fields.in.columns"), WRAPPING_FIELDS_VARIABLES_GROUPS); - addOption("ALIGN_CONSECUTIVE_VARIABLE_DECLARATIONS", ApplicationBundle.message("wrapping.align.variables.in.columns"), WRAPPING_FIELDS_VARIABLES_GROUPS); - - addOption("TERNARY_OPERATION_WRAP", WRAPPING_TERNARY_OPERATION, WRAP_OPTIONS, WRAP_VALUES); - addOption("ALIGN_MULTILINE_TERNARY_OPERATION", ApplicationBundle.message("wrapping.align.when.multiline"), WRAPPING_TERNARY_OPERATION); - addOption("TERNARY_OPERATION_SIGNS_ON_NEXT_LINE", ApplicationBundle.message("wrapping.quest.and.colon.signs.on.next.line"), WRAPPING_TERNARY_OPERATION); - - addOption("ARRAY_INITIALIZER_WRAP", WRAPPING_ARRAY_INITIALIZER, WRAP_OPTIONS, WRAP_VALUES); - addOption("ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION", ApplicationBundle.message("wrapping.align.when.multiline"), WRAPPING_ARRAY_INITIALIZER); - addOption("ARRAY_INITIALIZER_LBRACE_ON_NEXT_LINE", ApplicationBundle.message("wrapping.new.line.after.lbrace"), WRAPPING_ARRAY_INITIALIZER); - addOption("ARRAY_INITIALIZER_RBRACE_ON_NEXT_LINE", ApplicationBundle.message("wrapping.rbrace.on.new.line"), WRAPPING_ARRAY_INITIALIZER); - - addOption("MODIFIER_LIST_WRAP", ApplicationBundle.message("wrapping.after.modifier.list"), WRAPPING_MODIFIER_LIST); - - addOption("ASSERT_STATEMENT_WRAP", WRAPPING_ASSERT_STATEMENT, WRAP_OPTIONS, WRAP_VALUES); - addOption("ASSERT_STATEMENT_COLON_ON_NEXT_LINE", ApplicationBundle.message("wrapping.colon.signs.on.next.line"), WRAPPING_ASSERT_STATEMENT); - - addOption("ENUM_CONSTANTS_WRAP", ApplicationBundle.message("wrapping.enum.constants"), WRAP_OPTIONS, WRAP_VALUES); - addOption("CLASS_ANNOTATION_WRAP", ApplicationBundle.message("wrapping.classes.annotation"), WRAP_OPTIONS, WRAP_VALUES); - addOption("METHOD_ANNOTATION_WRAP", ApplicationBundle.message("wrapping.methods.annotation"), WRAP_OPTIONS, WRAP_VALUES); - addOption("FIELD_ANNOTATION_WRAP", ApplicationBundle.message("wrapping.fields.annotation"), WRAP_OPTIONS, WRAP_VALUES); - addOption("PARAMETER_ANNOTATION_WRAP", ApplicationBundle.message("wrapping.parameters.annotation"), WRAP_OPTIONS, WRAP_VALUES); - addOption("VARIABLE_ANNOTATION_WRAP", ApplicationBundle.message("wrapping.local.variables.annotation"), WRAP_OPTIONS, WRAP_VALUES); + for (Map.Entry> entry: + CodeStyleSettingPresentation.getStandardSettings(getSettingsType()).entrySet()) { + CodeStyleSettingPresentation.SettingsGroup group = entry.getKey(); + for (CodeStyleSettingPresentation setting: entry.getValue()) { + //TODO this is ugly, but is the fastest way to make Options UI API and settings representation API connect + String fieldName = setting.getFieldName(); + String uiName = setting.getUiName(); + if (setting instanceof CodeStyleBoundedIntegerSettingPresentation) { + CodeStyleBoundedIntegerSettingPresentation intSetting = (CodeStyleBoundedIntegerSettingPresentation) setting; + int defaultValue = intSetting.getDefaultValue(); + addOption(fieldName, uiName, group.name, intSetting.getLowerBound(), intSetting.getUpperBound(), defaultValue, intSetting.getValueUiName( + defaultValue)); + } else if (setting instanceof CodeStyleSelectSettingPresentation) { + CodeStyleSelectSettingPresentation selectSetting = (CodeStyleSelectSettingPresentation) setting; + addOption(fieldName, uiName, group.name, selectSetting.getOptions(), selectSetting.getValues()); + } else { + addOption(fieldName, uiName, group.name); + } + } + } } protected SettingsGroup getAssociatedSettingsGroup(String fieldName) { diff --git a/platform/lang-impl/src/com/intellij/psi/codeStyle/extractor/ExtractCodeStyleAction.java b/platform/lang-impl/src/com/intellij/psi/codeStyle/extractor/ExtractCodeStyleAction.java new file mode 100644 index 000000000000..e6d4950f8d1b --- /dev/null +++ b/platform/lang-impl/src/com/intellij/psi/codeStyle/extractor/ExtractCodeStyleAction.java @@ -0,0 +1,212 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.codeStyle.extractor; + +import com.intellij.lang.Language; +import com.intellij.lang.LanguageFormatting; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.extensions.Extensions; +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.MessageType; +import com.intellij.openapi.ui.popup.Balloon; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.wm.IdeFrame; +import com.intellij.openapi.wm.WindowManager; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.*; +import com.intellij.psi.codeStyle.extractor.differ.LangCodeStyleExtractor; +import com.intellij.psi.codeStyle.extractor.ui.ExtractedSettingsDialog; +import com.intellij.psi.impl.source.codeStyle.CodeStyleSchemesImpl; +import com.intellij.ui.BalloonLayout; +import com.intellij.psi.codeStyle.extractor.processor.CodeStyleDeriveProcessor; +import com.intellij.psi.codeStyle.extractor.processor.GenProcessor; +import com.intellij.psi.codeStyle.extractor.ui.CodeStyleSettingsNameProvider; +import com.intellij.psi.codeStyle.extractor.values.Value; +import com.intellij.psi.codeStyle.extractor.values.ValuesExtractionResult; +import org.jetbrains.annotations.NotNull; + + +import javax.swing.*; +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; +import java.awt.*; +import java.util.List; +import java.util.Map; + +public class ExtractCodeStyleAction extends AnAction implements DumbAware { + + @Override + public void actionPerformed(AnActionEvent e) { + DataContext dataContext = e.getDataContext(); + final Project project = CommonDataKeys.PROJECT.getData(dataContext); + if (project == null) return; + Editor editor = CommonDataKeys.EDITOR.getData(dataContext); + final VirtualFile[] files = CommonDataKeys.VIRTUAL_FILE_ARRAY.getData(dataContext); + PsiFile file = null; + if (editor == null && files != null && files.length == 1 && !files[0].isDirectory()) { + file = PsiManager.getInstance(project).findFile(files[0]); + } else if (editor != null) { + file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()); + } + if (file == null) return; + + Language language = file.getLanguage(); + + final LangCodeStyleExtractor extractor = LangCodeStyleExtractor.EXTENSION.forLanguage(language); + if (extractor == null) return; + + final CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(project); + + final CodeStyleDeriveProcessor genProcessor = new GenProcessor(extractor); + + final PsiFile finalFile = file; + final Task.Backgroundable task = new Task.Backgroundable(project, "Code style extractor", true) { + @Override + public void run(@NotNull ProgressIndicator indicator) { + try { + CodeStyleSettings cloneSettings = settings.clone(); + + Map backup = genProcessor.backupValues(cloneSettings, finalFile.getLanguage()); + + ValuesExtractionResult res = genProcessor.runWithProgress(project, cloneSettings, finalFile, indicator); + + reportResult(res, project, cloneSettings, finalFile, backup); + } + catch (ProcessCanceledException e) { + Utils.logError("Code extraction was canceled"); + } + catch (Throwable t) { + Utils.logError("Unexpected exception:\n" + t); + } + } + }; + ProgressManager.getInstance().run(task); + } + + public void reportResult(final ValuesExtractionResult forSelection, final Project project, + final CodeStyleSettings cloneSettings, final PsiFile file, final Map backup) { + final Balloon balloon = JBPopupFactory.getInstance() + .createHtmlTextBalloonBuilder( + "Formatting Options were extracted
Apply Details...", + MessageType.INFO, + new HyperlinkListener() { + @Override + public void hyperlinkUpdate(HyperlinkEvent e) { + if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + boolean apply = "apply".equals(e.getDescription()); + ExtractedSettingsDialog myDialog = null; + if (!apply) { + final List values = forSelection.getValues(); + final LanguageCodeStyleSettingsProvider[] providers = Extensions.getExtensions( + LanguageCodeStyleSettingsProvider.EP_NAME); + Language language = file.getLanguage(); + CodeStyleSettingsNameProvider nameProvider = new CodeStyleSettingsNameProvider(); + for (final LanguageCodeStyleSettingsProvider provider : providers) { + Language target = provider.getLanguage(); + if (target.equals(language)) { + //this is our language + nameProvider.addSettings(provider); + myDialog = new ExtractedSettingsDialog(project, nameProvider, values); + apply = myDialog.showAndGet(); + break; + } + } + } + if (apply && myDialog != null) { + //create new settings named after the file + final ExtractedSettingsDialog finalMyDialog = myDialog; + forSelection.applyConditioned(new Condition() { + @Override + public boolean value(Value value) { + return finalMyDialog.valueIsSelectedInTree(value); + } + }, backup); + CodeStyleScheme derivedScheme = CodeStyleSchemes.getInstance().createNewScheme("Derived from " + file.getName(), null); + derivedScheme.getCodeStyleSettings().copyFrom(cloneSettings); + CodeStyleSchemes.getInstance().addScheme(derivedScheme); + ((CodeStyleSchemesImpl) CodeStyleSchemes.getInstance()).getSchemeManager().setCurrent(derivedScheme); + CodeStyleSettingsManager.getInstance(project).PREFERRED_PROJECT_CODE_STYLE = derivedScheme.getName(); + } + } + + } + } + ).setDisposable(ApplicationManager.getApplication()).setShowCallout(false).setFadeoutTime(0). + setShowCallout(false).setAnimationCycle(0).setHideOnClickOutside(false).setHideOnKeyOutside(false). + setCloseButtonEnabled(true).setHideOnLinkClick(true).createBalloon(); + + ApplicationManager.getApplication(). + + invokeLater(new Runnable() { + @Override + public void run () { + Window window = WindowManager.getInstance().getFrame(project); + if (window == null) { + window = JOptionPane.getRootFrame(); + } + if (window instanceof IdeFrame) { + BalloonLayout layout = ((IdeFrame) window).getBalloonLayout(); + if (layout != null) { + layout.add(balloon); + } + } + } + } + + ); + } + + @Override + public void update(AnActionEvent event){ + Presentation presentation = event.getPresentation(); + DataContext dataContext = event.getDataContext(); + Project project = CommonDataKeys.PROJECT.getData(dataContext); + if (project == null){ + presentation.setEnabled(false); + return; + } + + Editor editor = CommonDataKeys.EDITOR.getData(dataContext); + + PsiFile file = null; + if (editor != null){ + file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()); + } else { + final VirtualFile[] files = CommonDataKeys.VIRTUAL_FILE_ARRAY.getData(dataContext); + if (files != null && files.length == 1 && !files[0].isDirectory()) { + file = PsiManager.getInstance(project).findFile(files[0]); + } + } + + if (file == null || file.getVirtualFile() == null) { + presentation.setEnabled(false); + return; + } + + if (LanguageFormatting.INSTANCE.forContext(file) != null) { + presentation.setEnabled(true); + } + } + } diff --git a/platform/lang-impl/src/com/intellij/psi/codeStyle/extractor/ui/CodeStyleSettingsNameProvider.java b/platform/lang-impl/src/com/intellij/psi/codeStyle/extractor/ui/CodeStyleSettingsNameProvider.java new file mode 100644 index 000000000000..843a8bb3768d --- /dev/null +++ b/platform/lang-impl/src/com/intellij/psi/codeStyle/extractor/ui/CodeStyleSettingsNameProvider.java @@ -0,0 +1,248 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.codeStyle.extractor.ui; + +import com.intellij.openapi.application.ApplicationBundle; +import com.intellij.openapi.util.Condition; +import com.intellij.psi.codeStyle.*; +import com.intellij.psi.codeStyle.extractor.values.Value; +import com.intellij.psi.codeStyle.presentation.CodeStyleSelectSettingPresentation; +import com.intellij.psi.codeStyle.presentation.CodeStyleSettingPresentation; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.psi.codeStyle.presentation.CodeStyleSettingPresentation.SettingsGroup; +import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider.SettingsType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * @author Roman.Shein + * @since 03.08.2015. + */ +public class CodeStyleSettingsNameProvider implements CodeStyleSettingsCustomizable { + + protected Map>> mySettings = + ContainerUtil.newHashMap(); + private Map>> standardSettings = + ContainerUtil.newHashMap(); + + public CodeStyleSettingsNameProvider() { + for (SettingsType settingsType : SettingsType.values()) { + standardSettings.put(settingsType, CodeStyleSettingPresentation.getStandardSettings(settingsType)); + } + } + + protected void addSetting(@NotNull SettingsGroup group, @NotNull CodeStyleSettingPresentation setting, @Nullable OptionAnchor anchor, + @Nullable String anchorFieldName) { + for (Map.Entry>> entry: mySettings.entrySet()) { + if (entry.getValue().containsKey(group)) { + addSetting(entry.getKey(), group, setting, anchor, anchorFieldName); + return; + } + } + addSetting(SettingsType.LANGUAGE_SPECIFIC, group, setting, anchor, anchorFieldName); + } + + protected void addSetting(@NotNull SettingsType settingsType, @NotNull SettingsGroup group, @NotNull CodeStyleSettingPresentation setting, + @Nullable OptionAnchor anchor, @Nullable String anchorFieldName) { + Map> groups = mySettings.get(settingsType); + if (groups == null) { + groups = ContainerUtil.newLinkedHashMap(); + } + List settingsList = groups.get(group); + if (settingsList == null) { + settingsList = ContainerUtil.newLinkedList(); + } + if (settingsList.contains(setting)) return; + if (anchor != null && anchorFieldName != null) { + CodeStyleSettingPresentation anchorSettingRepresentation = new CodeStyleSettingPresentation(anchorFieldName, anchorFieldName); + int insertIndex = settingsList.indexOf(anchorSettingRepresentation); + if (insertIndex < 0) { + insertIndex = settingsList.size(); + } else { + switch (anchor) { + case BEFORE: + break; + case AFTER: + insertIndex++; + break; + case NONE: + insertIndex = settingsList.size(); + } + } + settingsList.add(insertIndex, setting); + } else { + settingsList.add(setting); + } + groups.put(group, settingsList); + } + + @Override + public void showAllStandardOptions() { + for (SettingsType settingsType : SettingsType.values()) { + Map> standardGroups = standardSettings.get(settingsType); + for (Map.Entry> entry : standardGroups.entrySet()) { + for (CodeStyleSettingPresentation setting: entry.getValue()) { + addSetting(settingsType, entry.getKey(), setting, null, null); + } + } + } + } + + @Override + public void showStandardOptions(String... optionNames) { + List options = Arrays.asList(optionNames); + for (SettingsType settingsType : SettingsType.values()) { + Map> standardGroups = standardSettings.get(settingsType); + for (Map.Entry> entry : standardGroups.entrySet()) { + for (CodeStyleSettingPresentation setting: entry.getValue()) { + if (options.contains(setting.getFieldName())) { + addSetting(settingsType, entry.getKey(), setting, null, null); + } + } + } + } + } + + @Override + public void showCustomOption(Class settingsClass, @NotNull String fieldName, @NotNull String title, @Nullable String groupName, Object... options) { + showCustomOption(settingsClass, fieldName, title, groupName, null, null, options); + } + + @Override + public void showCustomOption(Class settingsClass, @NotNull String fieldName, @NotNull String title, + @Nullable String groupName, @Nullable OptionAnchor anchor, @Nullable String anchorFieldName, Object... options) { + if (options.length == 2) { + addSetting(new SettingsGroup(groupName), new CodeStyleSelectSettingPresentation(fieldName, title, (int[])options[1], + (String[])options[0]), anchor, anchorFieldName); + } else { + addSetting(new SettingsGroup(groupName), new CodeStyleSettingPresentation(fieldName, title), anchor, anchorFieldName); + } + } + + @Override + public void renameStandardOption(String fieldName, String newTitle) { + for (SettingsType settingsType : SettingsType.values()) { + Map> standardGroups = mySettings.get(settingsType); + if (standardGroups == null) { + continue; + } + for (Map.Entry> entry : standardGroups.entrySet()) { + for (CodeStyleSettingPresentation setting: entry.getValue()) { + if (setting.getFieldName().equals(fieldName)) { + setting.setUiName(newTitle); + return; + } + } + } + } + } + + @Override + public void moveStandardOption(String fieldName, String newGroup) { + for (SettingsType settingsType : SettingsType.values()) { + Map> standardGroups = mySettings.get(settingsType); + if (standardGroups == null) { + standardGroups = ContainerUtil.newLinkedHashMap(); + mySettings.put(settingsType, standardGroups); + } + for (Map.Entry> entry : standardGroups.entrySet()) { + CodeStyleSettingPresentation moveSetting = null; + for (CodeStyleSettingPresentation setting: entry.getValue()) { + if (setting.getFieldName().equals(fieldName)) { + moveSetting = setting; + break; + } + } + if (moveSetting != null) { + entry.getValue().remove(moveSetting); + addSetting(new SettingsGroup(newGroup), moveSetting, null, null); + } + } + } + } + + public static String getSettingsTypeName(LanguageCodeStyleSettingsProvider.SettingsType settingsType) { + switch (settingsType) { + case BLANK_LINES_SETTINGS: return ApplicationBundle.message("title.blank.lines"); + case SPACING_SETTINGS: return ApplicationBundle.message("title.spaces"); + case WRAPPING_AND_BRACES_SETTINGS: return ApplicationBundle.message("wrapping.and.braces"); + case INDENT_SETTINGS: return ApplicationBundle.message("title.tabs.and.indents"); + case LANGUAGE_SPECIFIC: return "Language-specific"; //TODO should load from ApplciationBundle here + default: throw new IllegalArgumentException("Unknown settings type: " + settingsType); + } + } + + public void addSettings(LanguageCodeStyleSettingsProvider provider) { + for (SettingsType settingsType : LanguageCodeStyleSettingsProvider.SettingsType.values()) { + provider.customizeSettings(this, settingsType); + } + } + + public static Value getValue(final CodeStyleSettingPresentation representation, List values) { + Value myValue = ContainerUtil.find(values, new Condition() { + @Override + public boolean value(Value value) { + return value.state == Value.STATE.SELECTED && value.name.equals(representation.getFieldName()); + //return value.name.equals(representation.getFieldName()); //TODO this is here only to test the UI!! + } + }); + return myValue; + } + + public String getSettings(List values) { + StringBuilder builder = new StringBuilder(); + for (SettingsType settingsType : LanguageCodeStyleSettingsProvider.SettingsType.values()) { + builder.append("
").append(getSettingsTypeName(settingsType)).append(""); + Map> groups = mySettings.get(settingsType); + if (groups != null) { + for (Map.Entry> entry : groups.entrySet()) { + boolean firstSettingGroupTop = entry.getKey().isNull(); + boolean groupReported = false; + for (final CodeStyleSettingPresentation setting : entry.getValue()) { + Value myValue = ContainerUtil.find(values, new Condition() { + @Override + public boolean value(Value value) { + return value.state == Value.STATE.SELECTED && value.name.equals(setting.getFieldName()); + } + }); + if (myValue == null) { + continue; + } + if (!groupReported) { + if (firstSettingGroupTop) { + builder.append(""); + } else { + builder.append("
").append(entry.getKey().name).append(""); + } + } + builder.append("
"); + String postNameSign = setting.getUiName().endsWith(":") ? " " : ": "; + builder.append(setting.getUiName()).append(postNameSign).append(setting.getValueUiName(myValue.value)); + if (!groupReported) { + if (firstSettingGroupTop) { + builder.append("
"); + } + } + groupReported = true; + } + } + } + } + return builder.toString(); + } +} diff --git a/platform/lang-impl/src/com/intellij/psi/codeStyle/extractor/ui/ExtractedSettingsDialog.java b/platform/lang-impl/src/com/intellij/psi/codeStyle/extractor/ui/ExtractedSettingsDialog.java new file mode 100644 index 000000000000..a87509ffe0de --- /dev/null +++ b/platform/lang-impl/src/com/intellij/psi/codeStyle/extractor/ui/ExtractedSettingsDialog.java @@ -0,0 +1,479 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.codeStyle.extractor.ui; + +import com.intellij.application.options.codeStyle.OptionTableWithPreviewPanel; +import com.intellij.openapi.application.ApplicationBundle; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.util.Condition; +import com.intellij.psi.codeStyle.presentation.CodeStyleSettingPresentation; +import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider; +import com.intellij.psi.codeStyle.extractor.values.Value; +import com.intellij.ui.SpeedSearchComparator; +import com.intellij.ui.TreeTableSpeedSearch; +import com.intellij.ui.components.JBScrollPane; +import com.intellij.ui.components.panels.HorizontalLayout; +import com.intellij.ui.treeStructure.treetable.*; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.ui.AbstractTableCellEditor; +import com.intellij.util.ui.ColumnInfo; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumn; +import javax.swing.tree.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.*; +import java.util.List; + +/** + * @author Roman.Shein + * @since 28.09.2015. + */ +public class ExtractedSettingsDialog extends DialogWrapper { + protected CodeStyleSettingsNameProvider myNameProvider; + protected List myValues; + protected DefaultMutableTreeNode myRoot; + + public ExtractedSettingsDialog(@Nullable Project project, + @NotNull CodeStyleSettingsNameProvider nameProvider, + @NotNull List values) { + super(project, false); + myNameProvider = nameProvider; + myValues = values; + setModal(true); + init(); + setTitle("Extracted Code Style Settings"); + } + + @Nullable + @Override + protected JComponent createCenterPanel() { + JComponent result = buildExtractedSettingsTree(); + return result; + } + + public boolean valueIsSelectedInTree(@NotNull Value value) { + if (myRoot == null) return false; + return valueIsSelectedInTree(myRoot, value); + } + + protected boolean valueIsSelectedInTree(@NotNull TreeNode startNode, @NotNull Value value) { + for (Enumeration children = startNode.children(); children.hasMoreElements();) { + Object child = children.nextElement(); + if (child instanceof SettingsTreeNode) { + SettingsTreeNode settingsChild = (SettingsTreeNode) child; + if (settingsChild.accepted && value.equals(settingsChild.myValue)) { + return true; + } + if (valueIsSelectedInTree(settingsChild, value)) return true; + } else if (child instanceof TreeNode) { + if (valueIsSelectedInTree((TreeNode) child, value)) return true; + } + } + return false; + } + + public static class SettingsTreeNode extends DefaultMutableTreeNode { + protected CodeStyleSettingPresentation myRepresentation; + protected boolean accepted = true; + protected final String valueString; + protected final boolean isGroupNode; + protected final String customTitle; + protected Value myValue; + + public SettingsTreeNode(String valueString, CodeStyleSettingPresentation representation, boolean isGroupNode, Value value) { + this(valueString, representation, isGroupNode, null, value); + } + + public SettingsTreeNode(String valueString, CodeStyleSettingPresentation representation, boolean isGroupNode, String customTitle, + Value value) { + this.valueString = valueString; + this.myRepresentation = representation; + this.isGroupNode = isGroupNode; + this.customTitle = customTitle; + this.myValue = value; + } + + public SettingsTreeNode(String title) { + this(title, null, true, null); + } + + public boolean isGroupOrTypeNode() { + return isGroupNode; + } + + @NotNull + public String getTitle() { + return customTitle != null ? customTitle : (myRepresentation == null ? valueString : myRepresentation.getUiName()); + } + + @Nullable + public String getValueString() { + return myRepresentation == null ? null : valueString; + } + } + + protected static ColumnInfo getTitleColumnInfo() { + return new ColumnInfo("TITLE") { + @Nullable + @Override + public Object valueOf(Object o) { + if (o instanceof SettingsTreeNode) { + return ((SettingsTreeNode) o).getTitle(); + } else { + return o.toString(); + } + } + + @Override + public Class getColumnClass() { + return TreeTableModel.class; + } + }; + } + + protected static class ValueRenderer implements TableCellRenderer { + private final JLabel myLabel = new JLabel(); + private final JCheckBox myCheckBox = new JCheckBox(); + private final JPanel myPanel = new JPanel(new HorizontalLayout(0)); + { + myPanel.add(myLabel); + myPanel.add(myCheckBox); + } + + @NotNull + @Override + public Component getTableCellRendererComponent(JTable table, + Object value, + boolean isSelected, + boolean hasFocus, + int row, + int column) { + if (table instanceof TreeTable) { + table.setEnabled(true); + DefaultMutableTreeNode valueNode = (DefaultMutableTreeNode)((TreeTable) table).getTree().getPathForRow(row).getLastPathComponent(); + if (valueNode instanceof SettingsTreeNode) { + SettingsTreeNode settingsNode = (SettingsTreeNode) valueNode; + myLabel.setText(settingsNode.getValueString()); + myCheckBox.setEnabled(true); + myCheckBox.setSelected(settingsNode.accepted); + } else { + myLabel.setBackground(table.getBackground()); + myCheckBox.setEnabled(false); + } + } + return myPanel; + } + } + + protected static class ValueEditor extends AbstractTableCellEditor { + + private final JLabel myLabel = new JLabel(); + private final JCheckBox myCheckBox = new JCheckBox(); + private final JPanel myPanel = new JPanel(new HorizontalLayout(0)); + { + myPanel.add(myLabel); + myPanel.add(myCheckBox); + } + + private SettingsTreeNode myCurrentNode; + private TreeTable myCurrentTree; + final ActionListener itemChoiceListener = new ActionListener() { + @Override + public void actionPerformed(@NotNull ActionEvent e) { + if (myCurrentNode != null) { + boolean wasChanged = myCurrentNode.accepted != myCheckBox.isSelected(); + myCurrentNode.accepted = myCheckBox.isSelected(); + if (wasChanged) { + updateAncestorsUi(myCurrentNode.accepted, myCurrentNode); + updateChildrenUi(myCurrentNode); + } + if (myCurrentTree != null) { + myCurrentTree.repaint(); + } + } + } + }; + + protected void updateAncestorsUi(boolean accepted, SettingsTreeNode node) { + TreeNode parent = node.getParent(); + if (parent != null && parent instanceof SettingsTreeNode) { + SettingsTreeNode settingsParent = (SettingsTreeNode) parent; + settingsParent.accepted = false; + if (!accepted) { + //propagate disabled settings upwards + updateAncestorsUi(false, settingsParent); + } else { + for (Enumeration children = parent.children(); children.hasMoreElements(); ) { + Object child = children.nextElement(); + if ((child instanceof SettingsTreeNode) && !((SettingsTreeNode) child).accepted) return; + } + settingsParent.accepted = true; + updateAncestorsUi(true, settingsParent); + } + } + } + + protected void updateChildrenUi(SettingsTreeNode node) { + for (Enumeration children = node.children(); children.hasMoreElements(); ) { + Object child = children.nextElement(); + if (child instanceof SettingsTreeNode) { + SettingsTreeNode settingsChild = (SettingsTreeNode) child; + settingsChild.accepted = node.accepted; + updateChildrenUi(settingsChild); + } + } + } + + public ValueEditor() { + myCheckBox.addActionListener(itemChoiceListener); + } + + @Override + public Object getCellEditorValue() { + return myCheckBox.isSelected(); + } + + @Override + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { + DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) ((TreeTable) table).getTree().getPathForRow(row).getLastPathComponent(); + if (treeNode instanceof SettingsTreeNode) { + myCurrentTree = (TreeTable) table; + myCurrentNode = (SettingsTreeNode) treeNode; + myLabel.setText(myCurrentNode.getValueString()); + myCheckBox.setSelected(myCurrentNode.accepted); + } + return myPanel; + } + } + + private static ValueRenderer myValueRenderer = new ValueRenderer(); + private static ValueEditor myValueEditor = new ValueEditor(); + + protected static ColumnInfo getValueColumnInfo() { + return new ColumnInfo("VALUE") { + @Nullable + @Override + public Object valueOf(Object o) { + if (o instanceof SettingsTreeNode) { + return ((SettingsTreeNode) o).getValueString(); + } else { + return null; + } + } + + @Override + public TableCellRenderer getRenderer(Object o) { + return myValueRenderer; + } + + public TableCellEditor getEditor(Object o) { + return myValueEditor; + } + + @Override + public boolean isCellEditable(Object o) { + return o instanceof SettingsTreeNode; + } + }; + } + + protected JComponent buildExtractedSettingsTree() { + + Collection unusedValues = ContainerUtil.newHashSet(myValues); + myRoot = new DefaultMutableTreeNode(); + for (Map.Entry>> typeEntry : myNameProvider.mySettings.entrySet()) { + DefaultMutableTreeNode settingsNode = null; + for (Map.Entry> groupEntry: typeEntry.getValue().entrySet()) { + CodeStyleSettingPresentation.SettingsGroup group = groupEntry.getKey(); + List representations = groupEntry.getValue(); + List children = ContainerUtil.emptyList(); + DefaultMutableTreeNode groupNode = null; + if (group.name == null && !representations.isEmpty()) { + //there is a setting with name coinciding with group name + if (representations.size() > 1) { + children = representations.subList(1, representations.size()); + } + CodeStyleSettingPresentation headRep = representations.get(0); + Value myValue = CodeStyleSettingsNameProvider.getValue(headRep, myValues); + if (myValue == null) { + //value was not found (was not selected) + groupNode = new SettingsTreeNode(headRep.getUiName()); + } else { + groupNode = new SettingsTreeNode(headRep.getUiName()); + groupNode.add(new SettingsTreeNode(headRep.getValueUiName(myValue.value), headRep, true, myValue)); + unusedValues.remove(myValue); + } + } else { + children = representations; + } + for (CodeStyleSettingPresentation representation: children) { + Value myValue = CodeStyleSettingsNameProvider.getValue(representation, myValues); + if (myValue != null) { + if (groupNode == null) { + groupNode = new SettingsTreeNode(group.name); + } + groupNode.add(new SettingsTreeNode(representation.getValueUiName(myValue.value), representation, false, myValue)); + unusedValues.remove(myValue); + } + } + if (groupNode != null && !groupNode.isLeaf()) { + if (settingsNode == null) { + settingsNode = new SettingsTreeNode(CodeStyleSettingsNameProvider.getSettingsTypeName(typeEntry.getKey())); + } + settingsNode.add(groupNode); + } + } + if (settingsNode != null) { + myRoot.add(settingsNode); + } + } + + //TODO: for now, settings without UI presentation are not displayed. Do something about it. + //unusedValues = ContainerUtil.filter(unusedValues, new Condition(){ + // @Override + // public boolean value(Value value) { + // return value.state == Value.STATE.SELECTED; + // } + //}); + // + //DefaultMutableTreeNode unnamedNode = null; + //for (Value value: unusedValues) { + // if (unnamedNode == null) { + // unnamedNode = new SettingsTreeNode("Settings without UI representation"); + // } + // unnamedNode.add(new SettingsTreeNode(value.value.toString(), null, false, value.name, value)); + //} + // + //if (unnamedNode != null) { + // myRoot.add(unnamedNode); + //} + + final ColumnInfo[] COLUMNS = new ColumnInfo[]{getTitleColumnInfo(), getValueColumnInfo()}; + + ListTreeTableModel model = new ListTreeTableModel(myRoot, COLUMNS); + final TreeTable treeTable = new TreeTable(model) { + @Override + public TreeTableCellRenderer createTableRenderer(TreeTableModel treeTableModel) { + TreeTableCellRenderer tableRenderer = super.createTableRenderer(treeTableModel); + UIUtil.setLineStyleAngled(tableRenderer); + tableRenderer.setRootVisible(false); + tableRenderer.setShowsRootHandles(true); + + return tableRenderer; + } + + @Override + public TableCellRenderer getCellRenderer(int row, int column) { + TreePath treePath = getTree().getPathForRow(row); + if (treePath == null) return super.getCellRenderer(row, column); + + Object node = treePath.getLastPathComponent(); + + TableCellRenderer renderer = COLUMNS[column].getRenderer(node); + return renderer == null ? super.getCellRenderer(row, column) : renderer; + } + + @Override + public TableCellEditor getCellEditor(int row, int column) { + TreePath treePath = getTree().getPathForRow(row); + if (treePath == null) return super.getCellEditor(row, column); + + Object node = treePath.getLastPathComponent(); + TableCellEditor editor = COLUMNS[column].getEditor(node); + return editor == null ? super.getCellEditor(row, column) : editor; + } + }; + new TreeTableSpeedSearch(treeTable).setComparator(new SpeedSearchComparator(false)); + + treeTable.setRootVisible(false); + + final JTree tree = treeTable.getTree(); + tree.setCellRenderer(myTitleRenderer); + tree.setShowsRootHandles(true); + treeTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + treeTable.setTableHeader(null); + + OptionTableWithPreviewPanel.expandTree(tree); + + treeTable.getColumnModel().getSelectionModel().setAnchorSelectionIndex(1); + treeTable.getColumnModel().getSelectionModel().setLeadSelectionIndex(1); + + int maxWidth = tree.getPreferredScrollableViewportSize().width + 10; + final TableColumn titleColumn = treeTable.getColumnModel().getColumn(0); + titleColumn.setPreferredWidth(maxWidth); + titleColumn.setMinWidth(maxWidth); + titleColumn.setMaxWidth(maxWidth); + titleColumn.setResizable(false); + + final Dimension valueSize = new JLabel(ApplicationBundle.message("option.table.sizing.text")).getPreferredSize(); + treeTable.setPreferredScrollableViewportSize(new Dimension(maxWidth + valueSize.width + 10, 20)); + treeTable.setBackground(UIUtil.getPanelBackground()); + treeTable.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10)); + + final Dimension screenSize = treeTable.getToolkit().getScreenSize(); + JBScrollPane scroller = new JBScrollPane(treeTable) { + @Override + public Dimension getMinimumSize() { + return super.getPreferredSize(); + } + }; + final Dimension preferredSize = new Dimension(Math.min(screenSize.width / 2, treeTable.getPreferredSize().width), + Math.min(screenSize.height / 2, treeTable.getPreferredSize().height)); + getRootPane().setPreferredSize(preferredSize); + return scroller; + } + + final TreeCellRenderer myTitleRenderer = new CellRenderer(); + + public static class CellRenderer implements TreeCellRenderer { + + private final JLabel myLabel = new JLabel(); + + @NotNull + @Override + public Component getTreeCellRendererComponent(JTree tree, + Object value, + boolean selected, + boolean expanded, + boolean leaf, + int row, + boolean hasFocus) { + if (value instanceof SettingsTreeNode) { + SettingsTreeNode node = (SettingsTreeNode) value; + myLabel.setText(node.getTitle()); + myLabel.setFont(node.isGroupOrTypeNode() ? myLabel.getFont().deriveFont(Font.BOLD) : myLabel.getFont().deriveFont(Font.PLAIN)); + } else { + myLabel.setText(value.toString()); + myLabel.setFont(myLabel.getFont().deriveFont(Font.BOLD)); + } + + Color foreground = selected ? UIUtil.getTableSelectionForeground() : UIUtil.getTableForeground(); + myLabel.setForeground(foreground); + + return myLabel; + } + } +} diff --git a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml index 575142e44c0c..ccd9ef10f45e 100644 --- a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml @@ -209,6 +209,9 @@ + + + diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index 9f03d8ffba72..eb9d9f7ae4aa 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -1031,6 +1031,7 @@ +