From 8728a099892a0e4ab7d9533c9ddda1a53574e0a2 Mon Sep 17 00:00:00 2001 From: "Gregory.Shrago" Date: Wed, 17 Oct 2012 22:32:05 +0400 Subject: [PATCH 1/4] don't show console TW on restart --- .../execution/console/LanguageConsoleImpl.java | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleImpl.java b/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleImpl.java index b53cbd1239a3..820741977e1d 100644 --- a/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleImpl.java @@ -48,7 +48,6 @@ import com.intellij.openapi.editor.impl.EditorFactoryImpl; import com.intellij.openapi.editor.markup.*; import com.intellij.openapi.fileEditor.*; import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; -import com.intellij.openapi.fileEditor.impl.EditorWindow; import com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl; import com.intellij.openapi.fileTypes.FileTypes; import com.intellij.openapi.project.Project; @@ -268,7 +267,7 @@ public class LanguageConsoleImpl implements Disposable, TypeSafeDataProvider { EmptyAction.registerActionShortcuts(myHistoryViewer.getComponent(), myConsoleEditor.getComponent()); } - private boolean isConsoleEditorEnabled() { + public boolean isConsoleEditorEnabled() { return myPanel.getComponentCount() > 1; } @@ -645,19 +644,11 @@ public class LanguageConsoleImpl implements Disposable, TypeSafeDataProvider { } public void setLanguage(Language language) { - int offset = getCurrentEditor().getCaretModel().getOffset(); myVirtualFile.setLanguage(language); // setViewProvider() call is required otherwise psiFile will stay the same! FileManager fileManager = ((PsiManagerEx)PsiManager.getInstance(myProject)).getFileManager(); fileManager.setViewProvider(myVirtualFile, fileManager.createFileViewProvider(myVirtualFile, true)); reparsePsiFile(); - if (!isConsoleEditorEnabled()) { - FileEditorManagerEx editorManagerEx = FileEditorManagerEx.getInstanceEx(myProject); - for (EditorWindow window : editorManagerEx.getWindows()) { - editorManagerEx.closeFile(myVirtualFile, window); - } - editorManagerEx.openTextEditor(new OpenFileDescriptor(myProject, myVirtualFile, offset), true); - } } public void setInputText(final String query) { From 2ce51a6bb54703d391b863e5b6b7304f70daa100 Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Mon, 15 Oct 2012 18:17:35 +0400 Subject: [PATCH 2/4] PredefinedCodeStyle is extension point now --- .../LanguageCodeStyleSettingsProvider.java | 3 ++- .../psi/codeStyle/PredefinedCodeStyle.java | 18 +++++++++++++--- .../options/TabbedLanguageCodeStylePanel.java | 21 +++++++++++++------ .../src/META-INF/LangExtensionPoints.xml | 2 ++ 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/LanguageCodeStyleSettingsProvider.java b/platform/lang-api/src/com/intellij/psi/codeStyle/LanguageCodeStyleSettingsProvider.java index 991abd19ce18..274d1bf08e11 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/LanguageCodeStyleSettingsProvider.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/LanguageCodeStyleSettingsProvider.java @@ -109,9 +109,10 @@ public abstract class LanguageCodeStyleSettingsProvider { } /** - * @return Array of predefined code styles (empty array by default). + * @deprecated use PredefinedCodeStyle extension point instead */ @NotNull + @Deprecated public PredefinedCodeStyle[] getPredefinedCodeStyles() { return PredefinedCodeStyle.EMPTY_ARRAY; } diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/PredefinedCodeStyle.java b/platform/lang-api/src/com/intellij/psi/codeStyle/PredefinedCodeStyle.java index 55436cf5a3a7..9229ceaa1e0b 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/PredefinedCodeStyle.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/PredefinedCodeStyle.java @@ -17,12 +17,15 @@ package com.intellij.psi.codeStyle; import com.intellij.lang.Language; +import com.intellij.openapi.extensions.ExtensionPointName; import org.jetbrains.annotations.NotNull; /** * @author Rustam Vishnyakov */ public abstract class PredefinedCodeStyle { + public static final ExtensionPointName EP_NAME = + ExtensionPointName.create("com.intellij.predefinedCodeStyle"); public final static PredefinedCodeStyle[] EMPTY_ARRAY = new PredefinedCodeStyle[]{}; private final String myName; @@ -46,9 +49,17 @@ public abstract class PredefinedCodeStyle { public boolean equals(Object obj) { if (!(obj instanceof PredefinedCodeStyle)) return false; PredefinedCodeStyle otherStyle = (PredefinedCodeStyle)obj; - return myName.equals(otherStyle.getName()); + return myName.equals(otherStyle.getName()) && + myLanguage.equals(otherStyle.getLanguage()); } - + + @Override + public int hashCode() { + int result = myName.hashCode(); + result = 31 * result + myLanguage.hashCode(); + return result; + } + public String getName() { return myName; } @@ -57,7 +68,8 @@ public abstract class PredefinedCodeStyle { public String toString() { return myName; } - + + @NotNull public Language getLanguage() { return myLanguage; } diff --git a/platform/lang-impl/src/com/intellij/application/options/TabbedLanguageCodeStylePanel.java b/platform/lang-impl/src/com/intellij/application/options/TabbedLanguageCodeStylePanel.java index e0b6099a396e..35a2ba0a6a00 100644 --- a/platform/lang-impl/src/com/intellij/application/options/TabbedLanguageCodeStylePanel.java +++ b/platform/lang-impl/src/com/intellij/application/options/TabbedLanguageCodeStylePanel.java @@ -40,9 +40,7 @@ import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; +import java.util.*; import java.util.List; /** @@ -357,9 +355,20 @@ public abstract class TabbedLanguageCodeStylePanel extends CodeStyleAbstractPane } private PredefinedCodeStyle[] getPredefinedStyles() { - LanguageCodeStyleSettingsProvider provider = LanguageCodeStyleSettingsProvider.forLanguage(getDefaultLanguage()); - if (provider == null) return new PredefinedCodeStyle[0]; - return provider.getPredefinedCodeStyles(); + final Language language = getDefaultLanguage(); + final List result = new ArrayList(); + + for (PredefinedCodeStyle codeStyle : PredefinedCodeStyle.EP_NAME.getExtensions()) { + if (codeStyle.getLanguage().equals(language)) { + result.add(codeStyle); + } + } + final LanguageCodeStyleSettingsProvider provider = LanguageCodeStyleSettingsProvider.forLanguage(getDefaultLanguage()); + + if (provider != null) { + result.addAll(Arrays.asList(provider.getPredefinedCodeStyles())); + } + return result.toArray(new PredefinedCodeStyle[result.size()]); } diff --git a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml index 936bbc58fe60..6e8a0f3da7ab 100644 --- a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml @@ -515,6 +515,8 @@ + + From 85459c84b5c3280ca1fa620e6b5d5a98d52fa36b Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Wed, 17 Oct 2012 21:41:50 +0400 Subject: [PATCH 3/4] support custom android xml formatter (part of IDEA-72907) --- plugins/android/src/META-INF/plugin.xml | 4 + .../formatter/AndroidXmlCodeStylePanel.java | 329 ++++++++++++++++++ .../AndroidXmlCodeStyleSettings.java | 194 +++++++++++ .../AndroidXmlCodeStyleSettingsProvider.java | 52 +++ .../formatter/AndroidXmlFormattingModel.java | 63 ++++ .../AndroidXmlFormattingModelBuilder.java | 81 +++++ .../android/formatter/AndroidXmlPolicy.java | 74 ++++ .../AndroidXmlPredefinedCodeStyle.java | 36 ++ .../ContextSpecificSettingsProviders.java | 42 +++ .../formatter/LayoutCodeStylePanel.form | 26 ++ .../formatter/MyFileSpecificPanel.form | 54 +++ .../testData/formatter/xml/layout1.xml | 31 ++ .../testData/formatter/xml/layout1_after.xml | 43 +++ .../testData/formatter/xml/layout2_after.xml | 31 ++ .../testData/formatter/xml/layout3_after.xml | 41 +++ .../testData/formatter/xml/layout4_after.xml | 31 ++ .../testData/formatter/xml/layout5_after.xml | 27 ++ .../testData/formatter/xml/layout6_after.xml | 31 ++ .../testData/formatter/xml/manifest1.xml | 14 + .../formatter/xml/manifest1_after.xml | 24 ++ .../formatter/xml/manifest2_after.xml | 14 + .../formatter/xml/manifest3_after.xml | 24 ++ .../formatter/xml/manifest4_after.xml | 22 ++ .../formatter/xml/manifest5_after.xml | 19 + .../formatter/xml/manifest6_after.xml | 21 ++ .../formatter/xml/manifest7_after.xml | 17 + .../testData/formatter/xml/preferences1.xml | 8 + .../formatter/xml/preferences1_after.xml | 13 + .../formatter/xml/preferences2_after.xml | 10 + .../testData/formatter/xml/selector1.xml | 5 + .../formatter/xml/selector1_after.xml | 7 + .../testData/formatter/xml/selector2.xml | 3 + .../formatter/xml/selector2_after.xml | 8 + .../formatter/xml/selector3_after.xml | 11 + .../testData/formatter/xml/shapeDrawable1.xml | 8 + .../formatter/xml/shapeDrawable1_after.xml | 17 + .../formatter/xml/shapeDrawable2_after.xml | 10 + .../testData/formatter/xml/values1.xml | 7 + .../testData/formatter/xml/values1_after.xml | 11 + .../testData/formatter/xml/values2_after.xml | 13 + .../testData/formatter/xml/values3_after.xml | 13 + .../testData/formatter/xml/values4.xml | 4 + .../testData/formatter/xml/values4_after.xml | 16 + .../testData/formatter/xml/values5_after.xml | 13 + .../android/AndroidXmlFormatterTest.java | 229 ++++++++++++ .../options/XmlCodeStyleMainPanel.java | 10 +- .../psi/formatter/xml/HtmlPolicy.java | 11 + .../psi/formatter/xml/SyntheticBlock.java | 13 +- .../formatter/xml/XmlFormattingPolicy.java | 5 + .../intellij/psi/formatter/xml/XmlPolicy.java | 11 + .../psi/formatter/xml/XmlTagBlock.java | 6 +- 51 files changed, 1802 insertions(+), 5 deletions(-) create mode 100644 plugins/android/src/org/jetbrains/android/formatter/AndroidXmlCodeStylePanel.java create mode 100644 plugins/android/src/org/jetbrains/android/formatter/AndroidXmlCodeStyleSettings.java create mode 100644 plugins/android/src/org/jetbrains/android/formatter/AndroidXmlCodeStyleSettingsProvider.java create mode 100644 plugins/android/src/org/jetbrains/android/formatter/AndroidXmlFormattingModel.java create mode 100644 plugins/android/src/org/jetbrains/android/formatter/AndroidXmlFormattingModelBuilder.java create mode 100644 plugins/android/src/org/jetbrains/android/formatter/AndroidXmlPolicy.java create mode 100644 plugins/android/src/org/jetbrains/android/formatter/AndroidXmlPredefinedCodeStyle.java create mode 100644 plugins/android/src/org/jetbrains/android/formatter/ContextSpecificSettingsProviders.java create mode 100644 plugins/android/src/org/jetbrains/android/formatter/LayoutCodeStylePanel.form create mode 100644 plugins/android/src/org/jetbrains/android/formatter/MyFileSpecificPanel.form create mode 100644 plugins/android/testData/formatter/xml/layout1.xml create mode 100644 plugins/android/testData/formatter/xml/layout1_after.xml create mode 100644 plugins/android/testData/formatter/xml/layout2_after.xml create mode 100644 plugins/android/testData/formatter/xml/layout3_after.xml create mode 100644 plugins/android/testData/formatter/xml/layout4_after.xml create mode 100644 plugins/android/testData/formatter/xml/layout5_after.xml create mode 100644 plugins/android/testData/formatter/xml/layout6_after.xml create mode 100644 plugins/android/testData/formatter/xml/manifest1.xml create mode 100644 plugins/android/testData/formatter/xml/manifest1_after.xml create mode 100644 plugins/android/testData/formatter/xml/manifest2_after.xml create mode 100644 plugins/android/testData/formatter/xml/manifest3_after.xml create mode 100644 plugins/android/testData/formatter/xml/manifest4_after.xml create mode 100644 plugins/android/testData/formatter/xml/manifest5_after.xml create mode 100644 plugins/android/testData/formatter/xml/manifest6_after.xml create mode 100644 plugins/android/testData/formatter/xml/manifest7_after.xml create mode 100644 plugins/android/testData/formatter/xml/preferences1.xml create mode 100644 plugins/android/testData/formatter/xml/preferences1_after.xml create mode 100644 plugins/android/testData/formatter/xml/preferences2_after.xml create mode 100644 plugins/android/testData/formatter/xml/selector1.xml create mode 100644 plugins/android/testData/formatter/xml/selector1_after.xml create mode 100644 plugins/android/testData/formatter/xml/selector2.xml create mode 100644 plugins/android/testData/formatter/xml/selector2_after.xml create mode 100644 plugins/android/testData/formatter/xml/selector3_after.xml create mode 100644 plugins/android/testData/formatter/xml/shapeDrawable1.xml create mode 100644 plugins/android/testData/formatter/xml/shapeDrawable1_after.xml create mode 100644 plugins/android/testData/formatter/xml/shapeDrawable2_after.xml create mode 100644 plugins/android/testData/formatter/xml/values1.xml create mode 100644 plugins/android/testData/formatter/xml/values1_after.xml create mode 100644 plugins/android/testData/formatter/xml/values2_after.xml create mode 100644 plugins/android/testData/formatter/xml/values3_after.xml create mode 100644 plugins/android/testData/formatter/xml/values4.xml create mode 100644 plugins/android/testData/formatter/xml/values4_after.xml create mode 100644 plugins/android/testData/formatter/xml/values5_after.xml create mode 100644 plugins/android/testSrc/org/jetbrains/android/AndroidXmlFormatterTest.java diff --git a/plugins/android/src/META-INF/plugin.xml b/plugins/android/src/META-INF/plugin.xml index 1c547a38ecfd..5affa71acfea 100644 --- a/plugins/android/src/META-INF/plugin.xml +++ b/plugins/android/src/META-INF/plugin.xml @@ -332,6 +332,10 @@ + + + + diff --git a/plugins/android/src/org/jetbrains/android/formatter/AndroidXmlCodeStylePanel.java b/plugins/android/src/org/jetbrains/android/formatter/AndroidXmlCodeStylePanel.java new file mode 100644 index 000000000000..afed24993fa2 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/formatter/AndroidXmlCodeStylePanel.java @@ -0,0 +1,329 @@ +package org.jetbrains.android.formatter; + +import com.intellij.application.options.CodeStyleAbstractPanel; +import com.intellij.ide.highlighter.XmlFileType; +import com.intellij.lang.xml.XMLLanguage; +import com.intellij.openapi.editor.colors.EditorColorsScheme; +import com.intellij.openapi.editor.highlighter.EditorHighlighter; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.ui.VerticalFlowLayout; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.ui.IdeBorderFactory; +import com.intellij.ui.components.JBCheckBox; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.List; + +/** +* @author Eugene.Kudelevsky +*/ +public class AndroidXmlCodeStylePanel extends CodeStyleAbstractPanel { + private final JPanel myPanel; + private final JBCheckBox myUseCustomSettings; + private final List myCodeStylePanels; + private final JPanel myFileSpecificCodeStylesPanel; + + AndroidXmlCodeStylePanel(CodeStyleSettings currentSettings, CodeStyleSettings settings) { + super(XMLLanguage.INSTANCE, currentSettings, settings); + + myPanel = new JPanel(new VerticalFlowLayout(VerticalFlowLayout.TOP, 0, 0, true, false)); + myPanel.setBorder(IdeBorderFactory.createEmptyBorder(2, 2, 2, 2)); + myUseCustomSettings = new JBCheckBox("Use custom formatting settings for Android XML files"); + myPanel.add(myUseCustomSettings); + + myCodeStylePanels = new ArrayList(); + myFileSpecificCodeStylesPanel = new JPanel(new VerticalFlowLayout(VerticalFlowLayout.TOP, 0, 0, true, false)); + myPanel.add(myFileSpecificCodeStylesPanel); + + myUseCustomSettings.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + UIUtil.setEnabled(myFileSpecificCodeStylesPanel, myUseCustomSettings.isSelected(), true); + } + }); + + myCodeStylePanels.add(new ManifestCodeStylePanel()); + myCodeStylePanels.add(new LayoutCodeStylePanel()); + myCodeStylePanels.add(new ValueResourcesCodeStylePanel()); + myCodeStylePanels.add(new OtherCodeStylePanel()); + + for (MyFileSpecificPanel panel : myCodeStylePanels) { + final JPanel titledPanel = new JPanel(new BorderLayout()); + titledPanel.setBorder(IdeBorderFactory.createTitledBorder(panel.getTitle())); + titledPanel.add(panel, BorderLayout.CENTER); + myFileSpecificCodeStylesPanel.add(titledPanel); + } + } + + @Override + protected int getRightMargin() { + return 0; + } + + @Nullable + @Override + protected EditorHighlighter createHighlighter(EditorColorsScheme scheme) { + return null; + } + + @NotNull + @Override + protected FileType getFileType() { + return XmlFileType.INSTANCE; + } + + @Nullable + @Override + protected String getPreviewText() { + return null; + } + + @Override + public void apply(CodeStyleSettings settings) { + final AndroidXmlCodeStyleSettings androidSettings = AndroidXmlCodeStyleSettings.getInstance(settings); + androidSettings.USE_CUSTOM_SETTINGS = myUseCustomSettings.isSelected(); + + for (MyFileSpecificPanel panel : myCodeStylePanels) { + panel.apply(androidSettings); + } + } + + @Override + public boolean isModified(CodeStyleSettings settings) { + final AndroidXmlCodeStyleSettings androidSettings = AndroidXmlCodeStyleSettings.getInstance(settings); + + if (androidSettings.USE_CUSTOM_SETTINGS != myUseCustomSettings.isSelected()) { + return true; + } + + for (MyFileSpecificPanel panel : myCodeStylePanels) { + if (panel.isModified(androidSettings)) { + return true; + } + } + return false; + } + + @Nullable + @Override + public JComponent getPanel() { + return myPanel; + } + + @Override + protected void resetImpl(CodeStyleSettings settings) { + final AndroidXmlCodeStyleSettings androidSettings = AndroidXmlCodeStyleSettings.getInstance(settings); + myUseCustomSettings.setSelected(androidSettings.USE_CUSTOM_SETTINGS); + UIUtil.setEnabled(myFileSpecificCodeStylesPanel, androidSettings.USE_CUSTOM_SETTINGS, true); + + for (MyFileSpecificPanel panel : myCodeStylePanels) { + panel.resetImpl(androidSettings); + } + } + + public abstract static class MyFileSpecificPanel extends JPanel { + private JPanel myPanel; + private JPanel myAdditionalOptionsPanel; + private JComboBox myWrapAttributesCombo; + + protected JBCheckBox myInsertLineBreakBeforeFirstAttributeCheckBox; + + private final String myTitle; + private final ContextSpecificSettingsProviders.Provider mySettingsProvider; + + protected MyFileSpecificPanel(String title, ContextSpecificSettingsProviders.Provider provider) { + myTitle = title; + mySettingsProvider = provider; + } + + protected void init() { + final JPanel panel = getAdditionalOptionsPanel(); + + if (panel != null) { + myAdditionalOptionsPanel.add(panel, BorderLayout.CENTER); + } + else { + myAdditionalOptionsPanel.setVisible(false); + } + setLayout(new BorderLayout()); + add(myPanel, BorderLayout.CENTER); + + fillWrappingCombo(myWrapAttributesCombo); + } + + @Nullable + public JPanel getAdditionalOptionsPanel() { + return null; + } + + public final void apply(AndroidXmlCodeStyleSettings settings) { + apply(mySettingsProvider.getSettings(settings)); + } + + protected void apply(T s) { + s.WRAP_ATTRIBUTES = ourWrappings[myWrapAttributesCombo.getSelectedIndex()]; + s.INSERT_LINE_BREAK_BEFORE_FIRST_ATTRIBUTE = myInsertLineBreakBeforeFirstAttributeCheckBox.isSelected(); + } + + public final boolean isModified(AndroidXmlCodeStyleSettings settings) { + return isModified(mySettingsProvider.getSettings(settings)); + } + + protected boolean isModified(T s) { + if (s.WRAP_ATTRIBUTES != ourWrappings[myWrapAttributesCombo.getSelectedIndex()]) { + return true; + } + if (s.INSERT_LINE_BREAK_BEFORE_FIRST_ATTRIBUTE != myInsertLineBreakBeforeFirstAttributeCheckBox.isSelected()) { + return true; + } + return false; + } + + protected final void resetImpl(AndroidXmlCodeStyleSettings settings) { + resetImpl(mySettingsProvider.getSettings(settings)); + } + + protected void resetImpl(T s) { + myWrapAttributesCombo.setSelectedIndex(getIndexForWrapping(s.WRAP_ATTRIBUTES)); + myInsertLineBreakBeforeFirstAttributeCheckBox.setSelected(s.INSERT_LINE_BREAK_BEFORE_FIRST_ATTRIBUTE); + } + + @NotNull + public String getTitle() { + return myTitle; + } + } + + private static class LayoutCodeStylePanel extends MyFileSpecificPanel { + private JPanel myPanel; + private JBCheckBox myInsertNewLineBeforeTagCheckBox; + + public LayoutCodeStylePanel() { + super("Layout Files", ContextSpecificSettingsProviders.LAYOUT); + init(); + } + + @Nullable + @Override + public JPanel getAdditionalOptionsPanel() { + return myPanel; + } + + @Override + protected boolean isModified(AndroidXmlCodeStyleSettings.LayoutSettings s) { + if (super.isModified(s)) { + return true; + } + return myInsertNewLineBeforeTagCheckBox.isSelected() != s.INSERT_BLANK_LINE_BEFORE_TAG; + } + + @Override + protected void resetImpl(AndroidXmlCodeStyleSettings.LayoutSettings s) { + super.resetImpl(s); + myInsertNewLineBeforeTagCheckBox.setSelected(s.INSERT_BLANK_LINE_BEFORE_TAG); + } + + @Override + protected void apply(AndroidXmlCodeStyleSettings.LayoutSettings s) { + super.apply(s); + s.INSERT_BLANK_LINE_BEFORE_TAG = myInsertNewLineBeforeTagCheckBox.isSelected(); + } + } + + public static class ManifestCodeStylePanel extends MyFileSpecificPanel { + private final JBCheckBox myGroupTagsCheckBox; + private JPanel myPanel; + + public ManifestCodeStylePanel() { + super("AndroidManifest.xml", ContextSpecificSettingsProviders.MANIFEST); + + myPanel = new JPanel(new VerticalFlowLayout(VerticalFlowLayout.TOP, 0, 0, true, false)); + myGroupTagsCheckBox = new JBCheckBox("Group tags with the same name"); + myPanel.add(myGroupTagsCheckBox); + + init(); + } + + @Nullable + @Override + public JPanel getAdditionalOptionsPanel() { + return myPanel; + } + + @Override + protected void apply(AndroidXmlCodeStyleSettings.ManifestSettings s) { + super.apply(s); + s.GROUP_TAGS_WITH_SAME_NAME = myGroupTagsCheckBox.isSelected(); + } + + @Override + protected boolean isModified(AndroidXmlCodeStyleSettings.ManifestSettings s) { + if (super.isModified(s)) { + return true; + } + return s.GROUP_TAGS_WITH_SAME_NAME != myGroupTagsCheckBox.isSelected(); + } + + @Override + protected void resetImpl(AndroidXmlCodeStyleSettings.ManifestSettings s) { + super.resetImpl(s); + myGroupTagsCheckBox.setSelected(s.GROUP_TAGS_WITH_SAME_NAME); + } + } + + public static class ValueResourcesCodeStylePanel extends MyFileSpecificPanel { + private final JBCheckBox myInsertLineBreaksAroundStyleCheckBox; + private JPanel myPanel; + + public ValueResourcesCodeStylePanel() { + super("Value Resource Files and Selectors", ContextSpecificSettingsProviders.VALUE_RESOURCE_FILE); + myPanel = new JPanel(new VerticalFlowLayout(VerticalFlowLayout.TOP, 0, 0, true, false)); + myInsertLineBreaksAroundStyleCheckBox = new JBCheckBox("Insert line breaks around style declaration"); + myPanel.add(myInsertLineBreaksAroundStyleCheckBox); + + init(); + myInsertLineBreakBeforeFirstAttributeCheckBox.setVisible(false); + } + + @Nullable + @Override + public JPanel getAdditionalOptionsPanel() { + return myPanel; + } + + @Override + protected void apply(AndroidXmlCodeStyleSettings.ValueResourceFileSettings s) { + super.apply(s); + s.INSERT_LINE_BREAKS_AROUND_STYLE = myInsertLineBreaksAroundStyleCheckBox.isSelected(); + } + + @Override + protected boolean isModified(AndroidXmlCodeStyleSettings.ValueResourceFileSettings s) { + if (super.isModified(s)) { + return true; + } + return s.INSERT_LINE_BREAKS_AROUND_STYLE != myInsertLineBreaksAroundStyleCheckBox.isSelected(); + } + + @Override + protected void resetImpl(AndroidXmlCodeStyleSettings.ValueResourceFileSettings s) { + super.resetImpl(s); + myInsertLineBreaksAroundStyleCheckBox.setSelected(s.INSERT_LINE_BREAKS_AROUND_STYLE); + } + } + + public static class OtherCodeStylePanel extends MyFileSpecificPanel { + public OtherCodeStylePanel() { + super("Other XML resource files", ContextSpecificSettingsProviders.OTHER); + init(); + myInsertLineBreakBeforeFirstAttributeCheckBox.setVisible(false); + } + } +} diff --git a/plugins/android/src/org/jetbrains/android/formatter/AndroidXmlCodeStyleSettings.java b/plugins/android/src/org/jetbrains/android/formatter/AndroidXmlCodeStyleSettings.java new file mode 100644 index 000000000000..35ec8e438d2b --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/formatter/AndroidXmlCodeStyleSettings.java @@ -0,0 +1,194 @@ +package org.jetbrains.android.formatter; + +import com.intellij.formatting.FormattingDocumentModel; +import com.intellij.openapi.util.InvalidDataException; +import com.intellij.openapi.util.JDOMExternalizable; +import com.intellij.openapi.util.WriteExternalException; +import com.intellij.psi.PsiElement; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.CommonCodeStyleSettings; +import com.intellij.psi.codeStyle.CustomCodeStyleSettings; +import com.intellij.psi.formatter.xml.XmlPolicy; +import com.intellij.psi.xml.XmlTag; +import com.intellij.util.xml.DomManager; +import com.intellij.util.xmlb.SkipDefaultValuesSerializationFilters; +import com.intellij.util.xmlb.XmlSerializer; +import org.jdom.Element; +import org.jetbrains.android.dom.resources.Style; + +/** + * @author Eugene.Kudelevsky + */ +public class AndroidXmlCodeStyleSettings extends CustomCodeStyleSettings { + public boolean USE_CUSTOM_SETTINGS = false; + + public LayoutSettings LAYOUT_SETTINGS = new LayoutSettings(); + public ManifestSettings MANIFEST_SETTINGS = new ManifestSettings(); + public ValueResourceFileSettings VALUE_RESOURCE_FILE_SETTINGS = new ValueResourceFileSettings(); + public OtherSettings OTHER_SETTINGS = new OtherSettings(); + + public AndroidXmlCodeStyleSettings(CodeStyleSettings container) { + super("AndroidXmlCodeStyleSettings", container); + } + + public static AndroidXmlCodeStyleSettings getInstance(CodeStyleSettings settings) { + return settings.getCustomSettings(AndroidXmlCodeStyleSettings.class); + } + + @Override + public Object clone() { + try { + final AndroidXmlCodeStyleSettings cloned = (AndroidXmlCodeStyleSettings)super.clone(); + cloned.LAYOUT_SETTINGS = (LayoutSettings)LAYOUT_SETTINGS.clone(); + cloned.MANIFEST_SETTINGS = (ManifestSettings)MANIFEST_SETTINGS.clone(); + cloned.VALUE_RESOURCE_FILE_SETTINGS = (ValueResourceFileSettings)VALUE_RESOURCE_FILE_SETTINGS.clone(); + cloned.OTHER_SETTINGS = (OtherSettings)OTHER_SETTINGS.clone(); + return cloned; + } + catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } + + public static class MySettings implements JDOMExternalizable, Cloneable { + public int WRAP_ATTRIBUTES; + public boolean INSERT_LINE_BREAK_BEFORE_FIRST_ATTRIBUTE; + + @Override + public void readExternal(Element element) throws InvalidDataException { + XmlSerializer.deserializeInto(this, element); + } + + @Override + public void writeExternal(Element element) throws WriteExternalException { + XmlSerializer.serializeInto(this, element, new SkipDefaultValuesSerializationFilters()); + } + + public XmlPolicy createXmlPolicy(CodeStyleSettings settings, FormattingDocumentModel documentModel) { + return new AndroidXmlPolicy(settings, this, documentModel); + } + + @Override + protected MySettings clone() throws CloneNotSupportedException { + try { + return (MySettings)super.clone(); + } + catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } + } + + public static class LayoutSettings extends MySettings { + public boolean INSERT_BLANK_LINE_BEFORE_TAG = true; + + { + WRAP_ATTRIBUTES = CommonCodeStyleSettings.WRAP_ALWAYS; + INSERT_LINE_BREAK_BEFORE_FIRST_ATTRIBUTE = true; + } + + @Override + public XmlPolicy createXmlPolicy(CodeStyleSettings settings, FormattingDocumentModel documentModel) { + return new AndroidXmlPolicy(settings, this, documentModel) { + @Override + public boolean insertLineBreakBeforeTag(XmlTag xmlTag) { + return INSERT_BLANK_LINE_BEFORE_TAG; + } + + @Override + public boolean insertLineBreakAfterTagBegin(XmlTag tag) { + return INSERT_BLANK_LINE_BEFORE_TAG; + } + }; + } + } + + public static class ManifestSettings extends MySettings { + public boolean GROUP_TAGS_WITH_SAME_NAME = true; + + { + WRAP_ATTRIBUTES = CommonCodeStyleSettings.WRAP_ALWAYS; + INSERT_LINE_BREAK_BEFORE_FIRST_ATTRIBUTE = true; + } + + @Override + public XmlPolicy createXmlPolicy(CodeStyleSettings settings, FormattingDocumentModel documentModel) { + return new AndroidXmlPolicy(settings, this, documentModel) { + @Override + public boolean insertLineBreakBeforeTag(XmlTag xmlTag) { + if (GROUP_TAGS_WITH_SAME_NAME) { + PsiElement element = getPrevSiblingElement(xmlTag); + + if (element instanceof XmlTag) { + final String name1 = ((XmlTag)element).getName(); + final String name2 = xmlTag.getName(); + + if (!name1.equals(name2)) { + element = getPrevSiblingElement(element); + + if (element instanceof XmlTag && ((XmlTag)element).getName().equals(name1)) { + return true; + } + element = getNextSiblingElement(xmlTag); + return element instanceof XmlTag && ((XmlTag)element).getName().equals(name2); + } + } + } + return false; + } + + @Override + public boolean insertLineBreakAfterTagBegin(XmlTag tag) { + return GROUP_TAGS_WITH_SAME_NAME && tag.getParentTag() == null; + } + }; + } + } + + public static class ValueResourceFileSettings extends MySettings { + public boolean INSERT_LINE_BREAKS_AROUND_STYLE = true; + + { + WRAP_ATTRIBUTES = CommonCodeStyleSettings.DO_NOT_WRAP; + INSERT_LINE_BREAK_BEFORE_FIRST_ATTRIBUTE = false; + } + + @Override + public XmlPolicy createXmlPolicy(CodeStyleSettings settings, FormattingDocumentModel documentModel) { + return new AndroidXmlPolicy(settings, this, documentModel) { + @Override + public boolean insertLineBreakAfterTagBegin(XmlTag tag) { + if (!INSERT_LINE_BREAKS_AROUND_STYLE) { + return false; + } + final XmlTag[] subTags = tag.getSubTags(); + return subTags.length != 0 && isStyleTag(subTags[0]); + } + + @Override + public boolean insertLineBreakBeforeTag(XmlTag xmlTag) { + if (!INSERT_LINE_BREAKS_AROUND_STYLE) { + return false; + } + if (isStyleTag(xmlTag)) { + return true; + } + final PsiElement sibling = getPrevSiblingElement(xmlTag); + return sibling instanceof XmlTag && isStyleTag((XmlTag)sibling); + } + + private boolean isStyleTag(XmlTag tag) { + return DomManager.getDomManager(tag.getProject()). + getDomElement(tag) instanceof Style; + } + }; + } + } + + public static class OtherSettings extends MySettings { + { + WRAP_ATTRIBUTES = CommonCodeStyleSettings.WRAP_ALWAYS; + INSERT_LINE_BREAK_BEFORE_FIRST_ATTRIBUTE = true; + } + } +} diff --git a/plugins/android/src/org/jetbrains/android/formatter/AndroidXmlCodeStyleSettingsProvider.java b/plugins/android/src/org/jetbrains/android/formatter/AndroidXmlCodeStyleSettingsProvider.java new file mode 100644 index 000000000000..ed4e820f94f1 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/formatter/AndroidXmlCodeStyleSettingsProvider.java @@ -0,0 +1,52 @@ +package org.jetbrains.android.formatter; + +import com.intellij.application.options.CodeStyleAbstractConfigurable; +import com.intellij.application.options.CodeStyleAbstractPanel; +import com.intellij.lang.Language; +import com.intellij.lang.xml.XMLLanguage; +import com.intellij.openapi.options.Configurable; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.CodeStyleSettingsProvider; +import com.intellij.psi.codeStyle.CustomCodeStyleSettings; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Eugene.Kudelevsky + */ +public class AndroidXmlCodeStyleSettingsProvider extends CodeStyleSettingsProvider { + @NotNull + @Override + public Configurable createSettingsPage(CodeStyleSettings settings, CodeStyleSettings originalSettings) { + return new CodeStyleAbstractConfigurable(settings, originalSettings, getConfigurableDisplayName()){ + protected CodeStyleAbstractPanel createPanel(final CodeStyleSettings settings) { + return new AndroidXmlCodeStylePanel(getCurrentSettings(), settings); + } + + public String getHelpTopic() { + return null; + } + }; + } + + @Override + public String getConfigurableDisplayName() { + return "Android"; + } + + @Override + public boolean hasSettingsPage() { + return false; + } + + @Nullable + @Override + public Language getLanguage() { + return XMLLanguage.INSTANCE; + } + + @Override + public CustomCodeStyleSettings createCustomSettings(CodeStyleSettings settings) { + return new AndroidXmlCodeStyleSettings(settings); + } +} diff --git a/plugins/android/src/org/jetbrains/android/formatter/AndroidXmlFormattingModel.java b/plugins/android/src/org/jetbrains/android/formatter/AndroidXmlFormattingModel.java new file mode 100644 index 000000000000..8700077d6192 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/formatter/AndroidXmlFormattingModel.java @@ -0,0 +1,63 @@ +package org.jetbrains.android.formatter; + +import com.intellij.formatting.*; +import com.intellij.lang.ASTNode; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.formatter.xml.XmlBlock; +import com.intellij.psi.formatter.xml.XmlPolicy; +import com.intellij.psi.formatter.xml.XmlTagBlock; +import org.jetbrains.annotations.NotNull; + +/** + * @author Eugene.Kudelevsky + */ +class AndroidXmlFormattingModel implements FormattingModel { + private final FormattingModel myModel; + private final Block myRootBlock; + + public AndroidXmlFormattingModel(FormattingModel model, + CodeStyleSettings settings, + final AndroidXmlCodeStyleSettings.MySettings customSettings) { + myModel = model; + final Block block = myModel.getRootBlock(); + + if (block instanceof XmlBlock) { + final XmlBlock b = (XmlBlock)block; + final XmlPolicy policy = customSettings.createXmlPolicy(settings, getDocumentModel()); + myRootBlock = new XmlBlock(b.getNode(), b.getWrap(), b.getAlignment(), policy, b.getIndent(), b.getTextRange()) { + @Override + protected XmlTagBlock createTagBlock(ASTNode child, Indent indent, Wrap wrap, Alignment alignment) { + return new XmlTagBlock(child, wrap, alignment, myXmlFormattingPolicy, + indent != null ? indent : Indent.getNoneIndent(), + isPreserveSpace()); + } + }; + } + else { + myRootBlock = block; + } + } + + @NotNull + public Block getRootBlock() { + return myRootBlock; + } + + @NotNull + public FormattingDocumentModel getDocumentModel() { + return myModel.getDocumentModel(); + } + + public TextRange replaceWhiteSpace(TextRange textRange, String whiteSpace) { + return myModel.replaceWhiteSpace(textRange, whiteSpace); + } + + public TextRange shiftIndentInsideRange(TextRange range, int indent) { + return myModel.shiftIndentInsideRange(range, indent); + } + + public void commitChanges() { + myModel.commitChanges(); + } +} diff --git a/plugins/android/src/org/jetbrains/android/formatter/AndroidXmlFormattingModelBuilder.java b/plugins/android/src/org/jetbrains/android/formatter/AndroidXmlFormattingModelBuilder.java new file mode 100644 index 000000000000..08c0b55b3379 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/formatter/AndroidXmlFormattingModelBuilder.java @@ -0,0 +1,81 @@ +package org.jetbrains.android.formatter; + +import com.intellij.formatting.CustomFormattingModelBuilder; +import com.intellij.formatting.FormattingModel; +import com.intellij.lang.ASTNode; +import com.intellij.lang.xml.XmlFormattingModelBuilder; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.xml.XmlFile; +import com.intellij.util.xml.DomFileDescription; +import com.intellij.util.xml.DomManager; +import org.jetbrains.android.dom.AndroidResourceDomFileDescription; +import org.jetbrains.android.dom.color.ColorDomFileDescription; +import org.jetbrains.android.dom.drawable.DrawableStateListDomFileDescription; +import org.jetbrains.android.dom.layout.LayoutDomFileDescription; +import org.jetbrains.android.dom.manifest.ManifestDomFileDescription; +import org.jetbrains.android.dom.resources.ResourcesDomFileDescription; +import org.jetbrains.android.facet.AndroidFacet; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Eugene.Kudelevsky + */ +public class AndroidXmlFormattingModelBuilder implements CustomFormattingModelBuilder { + private final XmlFormattingModelBuilder myXmlFormattingModelBuilder = new XmlFormattingModelBuilder(); + + @Override + public boolean isEngagedToFormat(PsiElement context) { + return getContextSpecificSettings(context) != null; + } + + @NotNull + @Override + public FormattingModel createModel(PsiElement element, CodeStyleSettings settings) { + final FormattingModel baseModel = myXmlFormattingModelBuilder.createModel(element, settings); + final AndroidXmlCodeStyleSettings baseSettings = AndroidXmlCodeStyleSettings.getInstance(settings); + + if (!baseSettings.USE_CUSTOM_SETTINGS) { + return baseModel; + } + final ContextSpecificSettingsProviders.Provider provider = getContextSpecificSettings(element); + final AndroidXmlCodeStyleSettings.MySettings s = provider != null ? provider.getSettings(baseSettings) : null; + return s != null ? new AndroidXmlFormattingModel(baseModel, settings, s) : baseModel; + } + + @Nullable + @Override + public TextRange getRangeAffectingIndent(PsiFile file, int offset, ASTNode elementAtOffset) { + return myXmlFormattingModelBuilder.getRangeAffectingIndent(file, offset, elementAtOffset); + } + + @Nullable + private static ContextSpecificSettingsProviders.Provider getContextSpecificSettings(PsiElement context) { + final PsiFile file = context.getContainingFile(); + + if (!(file instanceof XmlFile) || + AndroidFacet.getInstance(file) == null) { + return null; + } + final DomFileDescription description = DomManager.getDomManager( + context.getProject()).getDomFileDescription((XmlFile)file); + if (description instanceof LayoutDomFileDescription) { + return ContextSpecificSettingsProviders.LAYOUT; + } + else if (description instanceof ManifestDomFileDescription) { + return ContextSpecificSettingsProviders.MANIFEST; + } + else if (description instanceof ResourcesDomFileDescription || + description instanceof DrawableStateListDomFileDescription || + description instanceof ColorDomFileDescription) { + return ContextSpecificSettingsProviders.VALUE_RESOURCE_FILE; + } + else if (description instanceof AndroidResourceDomFileDescription) { + return ContextSpecificSettingsProviders.OTHER; + } + return null; + } +} diff --git a/plugins/android/src/org/jetbrains/android/formatter/AndroidXmlPolicy.java b/plugins/android/src/org/jetbrains/android/formatter/AndroidXmlPolicy.java new file mode 100644 index 000000000000..8aa838bb99bf --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/formatter/AndroidXmlPolicy.java @@ -0,0 +1,74 @@ +package org.jetbrains.android.formatter; + +import com.intellij.formatting.FormattingDocumentModel; +import com.intellij.formatting.WrapType; +import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiElement; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.formatter.FormatterUtil; +import com.intellij.psi.formatter.xml.XmlPolicy; +import com.intellij.psi.impl.source.SourceTreeToPsiMap; +import com.intellij.psi.xml.XmlAttribute; +import com.intellij.psi.xml.XmlTag; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** +* @author Eugene.Kudelevsky +*/ +public class AndroidXmlPolicy extends XmlPolicy { + private final AndroidXmlCodeStyleSettings.MySettings myCustomSettings; + + public AndroidXmlPolicy(CodeStyleSettings settings, + AndroidXmlCodeStyleSettings.MySettings customSettings, + FormattingDocumentModel documentModel) { + super(settings, documentModel); + myCustomSettings = customSettings; + } + + @Override + public WrapType getWrappingTypeForTagBegin(XmlTag tag) { + final PsiElement element = getNextSiblingElement(tag); + + if (element instanceof XmlTag && insertLineBreakBeforeTag((XmlTag)element)) { + return WrapType.NORMAL; + } + return super.getWrappingTypeForTagBegin(tag); + } + + @Override + public int getAttributesWrap() { + return myCustomSettings.WRAP_ATTRIBUTES; + } + + @Override + public boolean insertLineBreakBeforeFirstAttribute(XmlAttribute attribute) { + if (!myCustomSettings.INSERT_LINE_BREAK_BEFORE_FIRST_ATTRIBUTE || + attribute.isNamespaceDeclaration()) { + return false; + } + return attribute.getParent().getAttributes().length > 1; + } + + @Nullable + protected static PsiElement getPrevSiblingElement(@NotNull PsiElement element) { + final PsiElement prev = element.getPrevSibling(); + ASTNode prevNode = SourceTreeToPsiMap.psiElementToTree(prev); + + while (prevNode != null && FormatterUtil.containsWhiteSpacesOnly(prevNode)) { + prevNode = prevNode.getTreePrev(); + } + return SourceTreeToPsiMap.treeElementToPsi(prevNode); + } + + @Nullable + protected static PsiElement getNextSiblingElement(@NotNull PsiElement element) { + final PsiElement next = element.getNextSibling(); + ASTNode nextNode = SourceTreeToPsiMap.psiElementToTree(next); + + while (nextNode != null && FormatterUtil.containsWhiteSpacesOnly(nextNode)) { + nextNode = nextNode.getTreeNext(); + } + return SourceTreeToPsiMap.treeElementToPsi(nextNode); + } +} diff --git a/plugins/android/src/org/jetbrains/android/formatter/AndroidXmlPredefinedCodeStyle.java b/plugins/android/src/org/jetbrains/android/formatter/AndroidXmlPredefinedCodeStyle.java new file mode 100644 index 000000000000..e3335f4741da --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/formatter/AndroidXmlPredefinedCodeStyle.java @@ -0,0 +1,36 @@ +package org.jetbrains.android.formatter; + +import com.intellij.ide.highlighter.XmlFileType; +import com.intellij.lang.xml.XMLLanguage; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.CommonCodeStyleSettings; +import com.intellij.psi.codeStyle.PredefinedCodeStyle; +import com.intellij.psi.formatter.xml.XmlCodeStyleSettings; + +/** + * @author Eugene.Kudelevsky + */ +public class AndroidXmlPredefinedCodeStyle extends PredefinedCodeStyle { + public AndroidXmlPredefinedCodeStyle() { + super("Android", XMLLanguage.INSTANCE); + } + + @Override + public void apply(CodeStyleSettings settings) { + final CommonCodeStyleSettings.IndentOptions indentOptions = settings.getIndentOptions(XmlFileType.INSTANCE); + indentOptions.CONTINUATION_INDENT_SIZE = indentOptions.INDENT_SIZE; + + XmlCodeStyleSettings xmlSettings = settings.getCustomSettings(XmlCodeStyleSettings.class); + xmlSettings.XML_ALIGN_ATTRIBUTES = false; + xmlSettings.XML_SPACE_INSIDE_EMPTY_TAG = true; + xmlSettings.XML_KEEP_LINE_BREAKS = false; + + final AndroidXmlCodeStyleSettings androidSettings = AndroidXmlCodeStyleSettings.getInstance(settings); + androidSettings.USE_CUSTOM_SETTINGS = true; + + androidSettings.LAYOUT_SETTINGS = new AndroidXmlCodeStyleSettings.LayoutSettings(); + androidSettings.MANIFEST_SETTINGS = new AndroidXmlCodeStyleSettings.ManifestSettings(); + androidSettings.VALUE_RESOURCE_FILE_SETTINGS = new AndroidXmlCodeStyleSettings.ValueResourceFileSettings(); + androidSettings.OTHER_SETTINGS = new AndroidXmlCodeStyleSettings.OtherSettings(); + } +} diff --git a/plugins/android/src/org/jetbrains/android/formatter/ContextSpecificSettingsProviders.java b/plugins/android/src/org/jetbrains/android/formatter/ContextSpecificSettingsProviders.java new file mode 100644 index 000000000000..553f02b4c795 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/formatter/ContextSpecificSettingsProviders.java @@ -0,0 +1,42 @@ +package org.jetbrains.android.formatter; + +/** + * @author Eugene.Kudelevsky + */ +public class ContextSpecificSettingsProviders { + public static final Provider LAYOUT = + new Provider() { + @Override + public AndroidXmlCodeStyleSettings.LayoutSettings getSettings(AndroidXmlCodeStyleSettings baseSettings) { + return baseSettings.LAYOUT_SETTINGS; + } + }; + + public static final Provider MANIFEST = + new Provider() { + @Override + public AndroidXmlCodeStyleSettings.ManifestSettings getSettings(AndroidXmlCodeStyleSettings baseSettings) { + return baseSettings.MANIFEST_SETTINGS; + } + }; + + public static final Provider VALUE_RESOURCE_FILE = + new Provider() { + @Override + public AndroidXmlCodeStyleSettings.ValueResourceFileSettings getSettings(AndroidXmlCodeStyleSettings baseSettings) { + return baseSettings.VALUE_RESOURCE_FILE_SETTINGS; + } + }; + + public static final Provider OTHER = + new Provider() { + @Override + public AndroidXmlCodeStyleSettings.OtherSettings getSettings(AndroidXmlCodeStyleSettings baseSettings) { + return baseSettings.OTHER_SETTINGS; + } + }; + + abstract static class Provider { + abstract T getSettings(AndroidXmlCodeStyleSettings baseSettings); + } +} diff --git a/plugins/android/src/org/jetbrains/android/formatter/LayoutCodeStylePanel.form b/plugins/android/src/org/jetbrains/android/formatter/LayoutCodeStylePanel.form new file mode 100644 index 000000000000..89f99ea5f62b --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/formatter/LayoutCodeStylePanel.form @@ -0,0 +1,26 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/plugins/android/src/org/jetbrains/android/formatter/MyFileSpecificPanel.form b/plugins/android/src/org/jetbrains/android/formatter/MyFileSpecificPanel.form new file mode 100644 index 000000000000..63c0d3eb810f --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/formatter/MyFileSpecificPanel.form @@ -0,0 +1,54 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/plugins/android/testData/formatter/xml/layout1.xml b/plugins/android/testData/formatter/xml/layout1.xml new file mode 100644 index 000000000000..a5295e5d96a0 --- /dev/null +++ b/plugins/android/testData/formatter/xml/layout1.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/android/testData/formatter/xml/layout1_after.xml b/plugins/android/testData/formatter/xml/layout1_after.xml new file mode 100644 index 000000000000..dd84dee1a0dc --- /dev/null +++ b/plugins/android/testData/formatter/xml/layout1_after.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/android/testData/formatter/xml/layout2_after.xml b/plugins/android/testData/formatter/xml/layout2_after.xml new file mode 100644 index 000000000000..41ec99deb35e --- /dev/null +++ b/plugins/android/testData/formatter/xml/layout2_after.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/android/testData/formatter/xml/layout3_after.xml b/plugins/android/testData/formatter/xml/layout3_after.xml new file mode 100644 index 000000000000..e3b5ab37472d --- /dev/null +++ b/plugins/android/testData/formatter/xml/layout3_after.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/android/testData/formatter/xml/layout4_after.xml b/plugins/android/testData/formatter/xml/layout4_after.xml new file mode 100644 index 000000000000..241fa64b6324 --- /dev/null +++ b/plugins/android/testData/formatter/xml/layout4_after.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/android/testData/formatter/xml/layout5_after.xml b/plugins/android/testData/formatter/xml/layout5_after.xml new file mode 100644 index 000000000000..953549f99b8b --- /dev/null +++ b/plugins/android/testData/formatter/xml/layout5_after.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/android/testData/formatter/xml/layout6_after.xml b/plugins/android/testData/formatter/xml/layout6_after.xml new file mode 100644 index 000000000000..72e14088fc00 --- /dev/null +++ b/plugins/android/testData/formatter/xml/layout6_after.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/android/testData/formatter/xml/manifest1.xml b/plugins/android/testData/formatter/xml/manifest1.xml new file mode 100644 index 000000000000..df7d9d312b1b --- /dev/null +++ b/plugins/android/testData/formatter/xml/manifest1.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/android/testData/formatter/xml/manifest1_after.xml b/plugins/android/testData/formatter/xml/manifest1_after.xml new file mode 100644 index 000000000000..abf15803b164 --- /dev/null +++ b/plugins/android/testData/formatter/xml/manifest1_after.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/android/testData/formatter/xml/manifest2_after.xml b/plugins/android/testData/formatter/xml/manifest2_after.xml new file mode 100644 index 000000000000..38d6ba117cb5 --- /dev/null +++ b/plugins/android/testData/formatter/xml/manifest2_after.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/android/testData/formatter/xml/manifest3_after.xml b/plugins/android/testData/formatter/xml/manifest3_after.xml new file mode 100644 index 000000000000..abf15803b164 --- /dev/null +++ b/plugins/android/testData/formatter/xml/manifest3_after.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/android/testData/formatter/xml/manifest4_after.xml b/plugins/android/testData/formatter/xml/manifest4_after.xml new file mode 100644 index 000000000000..53c70e59df39 --- /dev/null +++ b/plugins/android/testData/formatter/xml/manifest4_after.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/android/testData/formatter/xml/manifest5_after.xml b/plugins/android/testData/formatter/xml/manifest5_after.xml new file mode 100644 index 000000000000..c654a6eb5343 --- /dev/null +++ b/plugins/android/testData/formatter/xml/manifest5_after.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/android/testData/formatter/xml/manifest6_after.xml b/plugins/android/testData/formatter/xml/manifest6_after.xml new file mode 100644 index 000000000000..53290ea01568 --- /dev/null +++ b/plugins/android/testData/formatter/xml/manifest6_after.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/android/testData/formatter/xml/manifest7_after.xml b/plugins/android/testData/formatter/xml/manifest7_after.xml new file mode 100644 index 000000000000..79f1c7bcd1ce --- /dev/null +++ b/plugins/android/testData/formatter/xml/manifest7_after.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/android/testData/formatter/xml/preferences1.xml b/plugins/android/testData/formatter/xml/preferences1.xml new file mode 100644 index 000000000000..3580d9f814ea --- /dev/null +++ b/plugins/android/testData/formatter/xml/preferences1.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/plugins/android/testData/formatter/xml/preferences1_after.xml b/plugins/android/testData/formatter/xml/preferences1_after.xml new file mode 100644 index 000000000000..49140c6f0ba8 --- /dev/null +++ b/plugins/android/testData/formatter/xml/preferences1_after.xml @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/plugins/android/testData/formatter/xml/preferences2_after.xml b/plugins/android/testData/formatter/xml/preferences2_after.xml new file mode 100644 index 000000000000..26add3c6cbcb --- /dev/null +++ b/plugins/android/testData/formatter/xml/preferences2_after.xml @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/plugins/android/testData/formatter/xml/selector1.xml b/plugins/android/testData/formatter/xml/selector1.xml new file mode 100644 index 000000000000..c1bd126b124f --- /dev/null +++ b/plugins/android/testData/formatter/xml/selector1.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/plugins/android/testData/formatter/xml/selector1_after.xml b/plugins/android/testData/formatter/xml/selector1_after.xml new file mode 100644 index 000000000000..f46f9fe2043c --- /dev/null +++ b/plugins/android/testData/formatter/xml/selector1_after.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/plugins/android/testData/formatter/xml/selector2.xml b/plugins/android/testData/formatter/xml/selector2.xml new file mode 100644 index 000000000000..0879f3d151f4 --- /dev/null +++ b/plugins/android/testData/formatter/xml/selector2.xml @@ -0,0 +1,3 @@ + + + diff --git a/plugins/android/testData/formatter/xml/selector2_after.xml b/plugins/android/testData/formatter/xml/selector2_after.xml new file mode 100644 index 000000000000..a767ee47241f --- /dev/null +++ b/plugins/android/testData/formatter/xml/selector2_after.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/plugins/android/testData/formatter/xml/selector3_after.xml b/plugins/android/testData/formatter/xml/selector3_after.xml new file mode 100644 index 000000000000..5e2b9c1ca35b --- /dev/null +++ b/plugins/android/testData/formatter/xml/selector3_after.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/plugins/android/testData/formatter/xml/shapeDrawable1.xml b/plugins/android/testData/formatter/xml/shapeDrawable1.xml new file mode 100644 index 000000000000..4fb2e6daa197 --- /dev/null +++ b/plugins/android/testData/formatter/xml/shapeDrawable1.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/plugins/android/testData/formatter/xml/shapeDrawable1_after.xml b/plugins/android/testData/formatter/xml/shapeDrawable1_after.xml new file mode 100644 index 000000000000..aa906cce1c5e --- /dev/null +++ b/plugins/android/testData/formatter/xml/shapeDrawable1_after.xml @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/plugins/android/testData/formatter/xml/shapeDrawable2_after.xml b/plugins/android/testData/formatter/xml/shapeDrawable2_after.xml new file mode 100644 index 000000000000..a5bfa72afec6 --- /dev/null +++ b/plugins/android/testData/formatter/xml/shapeDrawable2_after.xml @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/plugins/android/testData/formatter/xml/values1.xml b/plugins/android/testData/formatter/xml/values1.xml new file mode 100644 index 000000000000..68f48182e1fc --- /dev/null +++ b/plugins/android/testData/formatter/xml/values1.xml @@ -0,0 +1,7 @@ + + + untitleduntitled + abacaba + diff --git a/plugins/android/testData/formatter/xml/values1_after.xml b/plugins/android/testData/formatter/xml/values1_after.xml new file mode 100644 index 000000000000..0451e75eaeff --- /dev/null +++ b/plugins/android/testData/formatter/xml/values1_after.xml @@ -0,0 +1,11 @@ + + + untitled + untitled + + aba + caba + + + + diff --git a/plugins/android/testData/formatter/xml/values2_after.xml b/plugins/android/testData/formatter/xml/values2_after.xml new file mode 100644 index 000000000000..5422b1d2c3b4 --- /dev/null +++ b/plugins/android/testData/formatter/xml/values2_after.xml @@ -0,0 +1,13 @@ + + + untitled + untitled + + aba + caba + + + + diff --git a/plugins/android/testData/formatter/xml/values3_after.xml b/plugins/android/testData/formatter/xml/values3_after.xml new file mode 100644 index 000000000000..908261d03d80 --- /dev/null +++ b/plugins/android/testData/formatter/xml/values3_after.xml @@ -0,0 +1,13 @@ + + + untitled + untitled + + aba + caba + + + + diff --git a/plugins/android/testData/formatter/xml/values4.xml b/plugins/android/testData/formatter/xml/values4.xml new file mode 100644 index 000000000000..049cbbcd25b9 --- /dev/null +++ b/plugins/android/testData/formatter/xml/values4.xml @@ -0,0 +1,4 @@ + + + untitleduntitled + diff --git a/plugins/android/testData/formatter/xml/values4_after.xml b/plugins/android/testData/formatter/xml/values4_after.xml new file mode 100644 index 000000000000..e32570c9597b --- /dev/null +++ b/plugins/android/testData/formatter/xml/values4_after.xml @@ -0,0 +1,16 @@ + + + + + + untitled + untitled + + + diff --git a/plugins/android/testData/formatter/xml/values5_after.xml b/plugins/android/testData/formatter/xml/values5_after.xml new file mode 100644 index 000000000000..dcc6aff61efc --- /dev/null +++ b/plugins/android/testData/formatter/xml/values5_after.xml @@ -0,0 +1,13 @@ + + + + untitled + untitled + + diff --git a/plugins/android/testSrc/org/jetbrains/android/AndroidXmlFormatterTest.java b/plugins/android/testSrc/org/jetbrains/android/AndroidXmlFormatterTest.java new file mode 100644 index 000000000000..8a73b4dec424 --- /dev/null +++ b/plugins/android/testSrc/org/jetbrains/android/AndroidXmlFormatterTest.java @@ -0,0 +1,229 @@ +package org.jetbrains.android; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.CodeStyleSettingsManager; +import com.intellij.psi.codeStyle.CommonCodeStyleSettings; +import com.intellij.psi.formatter.xml.XmlCodeStyleSettings; +import org.jetbrains.android.formatter.AndroidXmlCodeStyleSettings; +import org.jetbrains.android.formatter.AndroidXmlPredefinedCodeStyle; + +import java.io.IOException; + +/** + * @author Eugene.Kudelevsky + */ +public class AndroidXmlFormatterTest extends AndroidTestCase { + private static final String BASE_PATH = "formatter/xml/"; + + private CodeStyleSettings mySettings; + + public AndroidXmlFormatterTest() { + super(false); + } + + public void testLayout1() throws Exception { + new AndroidXmlPredefinedCodeStyle().apply(mySettings); + doTestLayout("layout1.xml"); + } + + public void testLayout2() throws Exception { + doTestLayout("layout1.xml"); + } + + public void testLayout3() throws Exception { + new AndroidXmlPredefinedCodeStyle().apply(mySettings); + final XmlCodeStyleSettings xmlSettings = mySettings.getCustomSettings(XmlCodeStyleSettings.class); + xmlSettings.XML_ATTRIBUTE_WRAP = CommonCodeStyleSettings.DO_NOT_WRAP; + xmlSettings.XML_KEEP_BLANK_LINES = 0; + doTestLayout("layout1.xml"); + } + + public void testLayout4() throws Exception { + new AndroidXmlPredefinedCodeStyle().apply(mySettings); + final XmlCodeStyleSettings xmlSettings = mySettings.getCustomSettings(XmlCodeStyleSettings.class); + xmlSettings.XML_ALIGN_ATTRIBUTES = true; + final AndroidXmlCodeStyleSettings androidSettings = mySettings.getCustomSettings(AndroidXmlCodeStyleSettings.class); + androidSettings.LAYOUT_SETTINGS.INSERT_LINE_BREAK_BEFORE_FIRST_ATTRIBUTE = false; + androidSettings.LAYOUT_SETTINGS.INSERT_BLANK_LINE_BEFORE_TAG = false; + doTestLayout("layout1.xml"); + } + + public void testLayout5() throws Exception { + new AndroidXmlPredefinedCodeStyle().apply(mySettings); + final AndroidXmlCodeStyleSettings androidSettings = mySettings.getCustomSettings(AndroidXmlCodeStyleSettings.class); + androidSettings.LAYOUT_SETTINGS.WRAP_ATTRIBUTES = CommonCodeStyleSettings.DO_NOT_WRAP; + doTestLayout("layout1.xml"); + } + + public void testLayout6() throws Exception { + new AndroidXmlPredefinedCodeStyle().apply(mySettings); + final XmlCodeStyleSettings xmlSettings = mySettings.getCustomSettings(XmlCodeStyleSettings.class); + xmlSettings.XML_ALIGN_ATTRIBUTES = false; + final AndroidXmlCodeStyleSettings androidSettings = mySettings.getCustomSettings(AndroidXmlCodeStyleSettings.class); + androidSettings.LAYOUT_SETTINGS.INSERT_LINE_BREAK_BEFORE_FIRST_ATTRIBUTE = false; + androidSettings.LAYOUT_SETTINGS.INSERT_BLANK_LINE_BEFORE_TAG = false; + doTestLayout("layout1.xml"); + } + + public void testManifest1() throws Exception { + new AndroidXmlPredefinedCodeStyle().apply(mySettings); + doTestManifest("manifest1.xml"); + } + + public void testManifest2() throws Exception { + final XmlCodeStyleSettings xmlSettings = mySettings.getCustomSettings(XmlCodeStyleSettings.class); + xmlSettings.XML_ATTRIBUTE_WRAP = CommonCodeStyleSettings.DO_NOT_WRAP; + doTestManifest("manifest1.xml"); + } + + public void testManifest3() throws Exception { + new AndroidXmlPredefinedCodeStyle().apply(mySettings); + final XmlCodeStyleSettings xmlSettings = mySettings.getCustomSettings(XmlCodeStyleSettings.class); + xmlSettings.XML_ATTRIBUTE_WRAP = CommonCodeStyleSettings.DO_NOT_WRAP; + xmlSettings.XML_KEEP_BLANK_LINES = 0; + doTestManifest("manifest1.xml"); + } + + public void testManifest4() throws Exception { + new AndroidXmlPredefinedCodeStyle().apply(mySettings); + final XmlCodeStyleSettings xmlSettings = mySettings.getCustomSettings(XmlCodeStyleSettings.class); + xmlSettings.XML_ALIGN_ATTRIBUTES = true; + final AndroidXmlCodeStyleSettings androidSettings = mySettings.getCustomSettings(AndroidXmlCodeStyleSettings.class); + androidSettings.MANIFEST_SETTINGS.INSERT_LINE_BREAK_BEFORE_FIRST_ATTRIBUTE = false; + doTestManifest("manifest1.xml"); + } + + public void testManifest5() throws Exception { + new AndroidXmlPredefinedCodeStyle().apply(mySettings); + final AndroidXmlCodeStyleSettings androidSettings = mySettings.getCustomSettings(AndroidXmlCodeStyleSettings.class); + androidSettings.MANIFEST_SETTINGS.WRAP_ATTRIBUTES = CommonCodeStyleSettings.DO_NOT_WRAP; + doTestManifest("manifest1.xml"); + } + + public void testManifest6() throws Exception { + new AndroidXmlPredefinedCodeStyle().apply(mySettings); + final AndroidXmlCodeStyleSettings androidSettings = mySettings.getCustomSettings(AndroidXmlCodeStyleSettings.class); + androidSettings.MANIFEST_SETTINGS.GROUP_TAGS_WITH_SAME_NAME = false; + doTestManifest("manifest1.xml"); + } + + public void testManifest7() throws Exception { + final XmlCodeStyleSettings xmlSettings = mySettings.getCustomSettings(XmlCodeStyleSettings.class); + xmlSettings.XML_ATTRIBUTE_WRAP = CommonCodeStyleSettings.WRAP_ON_EVERY_ITEM; + doTestManifest("manifest1.xml"); + } + + public void testValues1() throws Exception { + new AndroidXmlPredefinedCodeStyle().apply(mySettings); + doTestValues("values1.xml"); + } + + public void testValues2() throws Exception { + new AndroidXmlPredefinedCodeStyle().apply(mySettings); + final AndroidXmlCodeStyleSettings androidSettings = mySettings.getCustomSettings(AndroidXmlCodeStyleSettings.class); + androidSettings.VALUE_RESOURCE_FILE_SETTINGS.INSERT_LINE_BREAK_BEFORE_FIRST_ATTRIBUTE = true; + doTestValues("values1.xml"); + } + + public void testValues3() throws Exception { + new AndroidXmlPredefinedCodeStyle().apply(mySettings); + final AndroidXmlCodeStyleSettings androidSettings = mySettings.getCustomSettings(AndroidXmlCodeStyleSettings.class); + androidSettings.VALUE_RESOURCE_FILE_SETTINGS.WRAP_ATTRIBUTES = CommonCodeStyleSettings.WRAP_ALWAYS; + doTestValues("values1.xml"); + } + + public void testValues4() throws Exception { + new AndroidXmlPredefinedCodeStyle().apply(mySettings); + doTestValues("values4.xml"); + } + + public void testValues5() throws Exception { + new AndroidXmlPredefinedCodeStyle().apply(mySettings); + final AndroidXmlCodeStyleSettings androidSettings = mySettings.getCustomSettings(AndroidXmlCodeStyleSettings.class); + androidSettings.VALUE_RESOURCE_FILE_SETTINGS.INSERT_LINE_BREAKS_AROUND_STYLE = false; + doTestValues("values4.xml"); + } + + public void testSelector1() throws Exception { + new AndroidXmlPredefinedCodeStyle().apply(mySettings); + doTest("selector1.xml", "res/drawable/selector.xml"); + } + + public void testSelector2() throws Exception { + new AndroidXmlPredefinedCodeStyle().apply(mySettings); + doTest("selector2.xml", "res/color/selector.xml"); + } + + public void testSelector3() throws Exception { + new AndroidXmlPredefinedCodeStyle().apply(mySettings); + final AndroidXmlCodeStyleSettings androidSettings = mySettings.getCustomSettings(AndroidXmlCodeStyleSettings.class); + androidSettings.VALUE_RESOURCE_FILE_SETTINGS.WRAP_ATTRIBUTES = CommonCodeStyleSettings.WRAP_ALWAYS; + doTest("selector2.xml", "res/color/selector.xml"); + } + + public void testShapeDrawable1() throws Exception { + new AndroidXmlPredefinedCodeStyle().apply(mySettings); + doTest("shapeDrawable1.xml", "res/drawable/drawable.xml"); + } + + public void testShapeDrawable2() throws Exception { + new AndroidXmlPredefinedCodeStyle().apply(mySettings); + final AndroidXmlCodeStyleSettings androidSettings = mySettings.getCustomSettings(AndroidXmlCodeStyleSettings.class); + androidSettings.OTHER_SETTINGS.WRAP_ATTRIBUTES = CommonCodeStyleSettings.DO_NOT_WRAP; + doTest("shapeDrawable1.xml", "res/drawable/drawable.xml"); + } + + public void testPreferences1() throws Exception { + new AndroidXmlPredefinedCodeStyle().apply(mySettings); + doTest("preferences1.xml", "res/xml/preferences.xml"); + } + + public void testPreferences2() throws Exception { + new AndroidXmlPredefinedCodeStyle().apply(mySettings); + final AndroidXmlCodeStyleSettings androidSettings = mySettings.getCustomSettings(AndroidXmlCodeStyleSettings.class); + androidSettings.OTHER_SETTINGS.WRAP_ATTRIBUTES = CommonCodeStyleSettings.DO_NOT_WRAP; + doTest("preferences1.xml", "res/xml/preferences.xml"); + } + + private void doTestLayout(String fileName) throws IOException { + createManifest(); + doTest(fileName, "res/layout/layout.xml"); + } + + private void doTestManifest(String fileName) { + doTest(fileName, "AndroidManifest.xml"); + } + + private void doTestValues(String fileName) { + doTest(fileName, "res/values/values.xml"); + } + + private void doTest(String fileName, String dstFileName) { + final VirtualFile f = myFixture.copyFileToProject(BASE_PATH + fileName, dstFileName); + myFixture.configureFromExistingVirtualFile(f); + + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + CodeStyleManager.getInstance(getProject()).reformat(myFixture.getFile()); + } + }); + myFixture.checkResultByFile(BASE_PATH + getTestName(true) + "_after.xml"); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + mySettings = CodeStyleSettingsManager.getSettings(getProject()).clone(); + CodeStyleSettingsManager.getInstance(getProject()).setTemporarySettings(mySettings); + } + + @Override + public void tearDown() throws Exception { + CodeStyleSettingsManager.getInstance(getProject()).dropTemporarySettings(); + super.tearDown(); + } +} diff --git a/xml/impl/src/com/intellij/application/options/XmlCodeStyleMainPanel.java b/xml/impl/src/com/intellij/application/options/XmlCodeStyleMainPanel.java index 1d24ea7563f6..e465fb04741a 100644 --- a/xml/impl/src/com/intellij/application/options/XmlCodeStyleMainPanel.java +++ b/xml/impl/src/com/intellij/application/options/XmlCodeStyleMainPanel.java @@ -15,10 +15,10 @@ */ package com.intellij.application.options; -import com.intellij.lang.Language; import com.intellij.lang.xml.XMLLanguage; +import com.intellij.openapi.extensions.Extensions; import com.intellij.psi.codeStyle.CodeStyleSettings; -import org.jetbrains.annotations.Nullable; +import com.intellij.psi.codeStyle.CodeStyleSettingsProvider; /** * @author Rustam Vishnyakov @@ -32,5 +32,11 @@ public class XmlCodeStyleMainPanel extends TabbedLanguageCodeStylePanel { protected void initTabs(CodeStyleSettings settings) { addIndentOptionsTab(settings); addTab(new CodeStyleXmlPanel(settings)); + + for (CodeStyleSettingsProvider provider : Extensions.getExtensions(CodeStyleSettingsProvider.EXTENSION_POINT_NAME)) { + if (provider.getLanguage() == XMLLanguage.INSTANCE && !provider.hasSettingsPage()) { + createTab(provider); + } + } } } diff --git a/xml/impl/src/com/intellij/psi/formatter/xml/HtmlPolicy.java b/xml/impl/src/com/intellij/psi/formatter/xml/HtmlPolicy.java index 57d12dcb9d31..48c1502c1cdc 100644 --- a/xml/impl/src/com/intellij/psi/formatter/xml/HtmlPolicy.java +++ b/xml/impl/src/com/intellij/psi/formatter/xml/HtmlPolicy.java @@ -24,6 +24,7 @@ import com.intellij.psi.TokenType; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.impl.source.SourceTreeToPsiMap; import com.intellij.psi.impl.source.tree.LeafElement; +import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlElementType; import com.intellij.psi.xml.XmlTag; @@ -88,6 +89,16 @@ public class HtmlPolicy extends XmlFormattingPolicy { return checkName(xmlTag, mySettings.HTML_ELEMENTS_TO_INSERT_NEW_LINE_BEFORE); } + @Override + public boolean insertLineBreakBeforeFirstAttribute(XmlAttribute attribute) { + return false; + } + + @Override + public boolean insertLineBreakAfterTagBegin(XmlTag tag) { + return false; + } + private boolean containsWhiteSpacesOnly(final ASTNode node) { if (node == null) return false; if (node.getElementType() == TokenType.WHITE_SPACE) return true; diff --git a/xml/impl/src/com/intellij/psi/formatter/xml/SyntheticBlock.java b/xml/impl/src/com/intellij/psi/formatter/xml/SyntheticBlock.java index 264426bf78a9..33e06e31f5b9 100644 --- a/xml/impl/src/com/intellij/psi/formatter/xml/SyntheticBlock.java +++ b/xml/impl/src/com/intellij/psi/formatter/xml/SyntheticBlock.java @@ -18,10 +18,13 @@ package com.intellij.psi.formatter.xml; import com.intellij.formatting.*; import com.intellij.lang.ASTNode; import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; import com.intellij.psi.formatter.common.AbstractBlock; import com.intellij.psi.tree.IElementType; +import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlElementType; import com.intellij.psi.xml.XmlTag; +import com.intellij.psi.xml.XmlTokenType; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -109,7 +112,15 @@ public class SyntheticBlock extends AbstractSyntheticBlock implements Block, Rea } if (type2 == XmlElementType.XML_ATTRIBUTE) { - return Spacing.createSpacing(1, 1, 0, myXmlFormattingPolicy.getShouldKeepLineBreaks(), myXmlFormattingPolicy.getKeepBlankLines()); + int minLineFeeds = 0; + + if (type1 == XmlTokenType.XML_NAME) { + final PsiElement psi2 = node2.getPsi(); + minLineFeeds = psi2 instanceof XmlAttribute && + myXmlFormattingPolicy.insertLineBreakBeforeFirstAttribute((XmlAttribute)psi2) + ? 1 : 0; + } + return Spacing.createSpacing(1, 1, minLineFeeds, myXmlFormattingPolicy.getShouldKeepLineBreaks(), myXmlFormattingPolicy.getKeepBlankLines()); } if (((AbstractXmlBlock)child1).isTextElement() && ((AbstractXmlBlock)child2).isTextElement()) { diff --git a/xml/impl/src/com/intellij/psi/formatter/xml/XmlFormattingPolicy.java b/xml/impl/src/com/intellij/psi/formatter/xml/XmlFormattingPolicy.java index 44ba07f43aff..ae6fb0be8114 100644 --- a/xml/impl/src/com/intellij/psi/formatter/xml/XmlFormattingPolicy.java +++ b/xml/impl/src/com/intellij/psi/formatter/xml/XmlFormattingPolicy.java @@ -25,6 +25,7 @@ import com.intellij.lang.LanguageFormatting; import com.intellij.openapi.util.Pair; import com.intellij.psi.PsiElement; import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlTag; import com.intellij.util.containers.HashMap; @@ -87,6 +88,10 @@ public abstract class XmlFormattingPolicy { public abstract boolean insertLineBreakBeforeTag(XmlTag xmlTag); + public abstract boolean insertLineBreakBeforeFirstAttribute(XmlAttribute attribute); + + public abstract boolean insertLineBreakAfterTagBegin(XmlTag tag); + public abstract boolean removeLineBreakBeforeTag(XmlTag xmlTag); public abstract boolean keepWhiteSpacesInsideTag(XmlTag tag); diff --git a/xml/impl/src/com/intellij/psi/formatter/xml/XmlPolicy.java b/xml/impl/src/com/intellij/psi/formatter/xml/XmlPolicy.java index 2d45dfa9de68..40bb6abce287 100644 --- a/xml/impl/src/com/intellij/psi/formatter/xml/XmlPolicy.java +++ b/xml/impl/src/com/intellij/psi/formatter/xml/XmlPolicy.java @@ -21,6 +21,7 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiWhiteSpace; import com.intellij.psi.PsiErrorElement; import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlTag; import com.intellij.psi.xml.XmlText; @@ -42,6 +43,16 @@ public class XmlPolicy extends XmlFormattingPolicy{ return false; } + @Override + public boolean insertLineBreakBeforeFirstAttribute(XmlAttribute attribute) { + return false; + } + + @Override + public boolean insertLineBreakAfterTagBegin(XmlTag tag) { + return false; + } + public boolean removeLineBreakBeforeTag(final XmlTag xmlTag) { return false; } diff --git a/xml/impl/src/com/intellij/psi/formatter/xml/XmlTagBlock.java b/xml/impl/src/com/intellij/psi/formatter/xml/XmlTagBlock.java index e8e7c71bf9aa..79b32c9218ea 100644 --- a/xml/impl/src/com/intellij/psi/formatter/xml/XmlTagBlock.java +++ b/xml/impl/src/com/intellij/psi/formatter/xml/XmlTagBlock.java @@ -279,11 +279,13 @@ public class XmlTagBlock extends AbstractXmlBlock{ } return Spacing.createSpacing(0, 0, 0, true, myXmlFormattingPolicy.getKeepBlankLines()); } else if (syntheticBlock1.isTagDescription() && syntheticBlock2.startsWithTag()) { - return Spacing.createSpacing(0, 0, 0, true, myXmlFormattingPolicy.getKeepBlankLines()); + return Spacing.createSpacing(0, 0, myXmlFormattingPolicy.insertLineBreakAfterTagBegin(getTag()) ? 2 : 0, + true, myXmlFormattingPolicy.getKeepBlankLines()); } else if (syntheticBlock1.insertLineFeedAfter()) { return Spacing.createSpacing(0,0,1,true,myXmlFormattingPolicy.getKeepBlankLines()); } else if (syntheticBlock1.endsWithTag() && syntheticBlock2.isTagDescription()) { - return Spacing.createSpacing(0, 0, 0, true, myXmlFormattingPolicy.getKeepBlankLines()); + return Spacing.createSpacing(0, 0, myXmlFormattingPolicy.insertLineBreakAfterTagBegin(getTag()) ? 2 : 0, + true, myXmlFormattingPolicy.getKeepBlankLines()); } else { return createDefaultSpace(true, true); } From a8c028a93bce10c7350a8bb17407f160deb98acb Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Wed, 17 Oct 2012 22:40:17 +0400 Subject: [PATCH 4/4] fix applying of predefined style when code style configurable is wrapped in a tab --- .../options/CodeStyleAbstractConfigurable.java | 10 ++++++---- .../options/TabbedLanguageCodeStylePanel.java | 11 ++++++++++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/application/options/CodeStyleAbstractConfigurable.java b/platform/lang-impl/src/com/intellij/application/options/CodeStyleAbstractConfigurable.java index 793790b0c7bd..58662d694d9b 100644 --- a/platform/lang-impl/src/com/intellij/application/options/CodeStyleAbstractConfigurable.java +++ b/platform/lang-impl/src/com/intellij/application/options/CodeStyleAbstractConfigurable.java @@ -62,14 +62,16 @@ public abstract class CodeStyleAbstractConfigurable implements Configurable, Opt @Override public void reset() { - if (myPanel != null) { - myPanel.reset(mySettings); - } + reset(mySettings); } public void resetFromClone(){ + reset(myCloneSettings); + } + + public void reset(CodeStyleSettings settings) { if (myPanel != null) { - myPanel.reset(myCloneSettings); + myPanel.reset(settings); } } diff --git a/platform/lang-impl/src/com/intellij/application/options/TabbedLanguageCodeStylePanel.java b/platform/lang-impl/src/com/intellij/application/options/TabbedLanguageCodeStylePanel.java index 35a2ba0a6a00..207e393d40c6 100644 --- a/platform/lang-impl/src/com/intellij/application/options/TabbedLanguageCodeStylePanel.java +++ b/platform/lang-impl/src/com/intellij/application/options/TabbedLanguageCodeStylePanel.java @@ -541,7 +541,16 @@ public abstract class TabbedLanguageCodeStylePanel extends CodeStyleAbstractPane @Override protected void resetImpl(CodeStyleSettings settings) { - myConfigurable.reset(); + if (myConfigurable instanceof CodeStyleAbstractConfigurable) { + // when a predefined style is chosen and the configurable is wrapped in a tab, + // we apply it to CLONED code style settings and then pass them to this method to reset, + // usual reset() won't work in such case + ((CodeStyleAbstractConfigurable)myConfigurable).reset(settings); + } + else { + // todo: support for other configurables + myConfigurable.reset(); + } } }