diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/match/ArrangementEntryType.java b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/match/ArrangementEntryType.java
index 773b7664f60b..654443211792 100644
--- a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/match/ArrangementEntryType.java
+++ b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/match/ArrangementEntryType.java
@@ -31,5 +31,7 @@ public enum ArrangementEntryType {
CONST, VAR, PROPERTY, EVENT_HANDLER, STATIC_INIT,
- NAMESPACE, TRAIT
+ NAMESPACE, TRAIT,
+
+ XML_TAG, XML_ATTRIBUTE
}
diff --git a/platform/platform-resources/src/META-INF/XmlPlugin.xml b/platform/platform-resources/src/META-INF/XmlPlugin.xml
index b08ebdc840c8..e8b62c5d4bab 100644
--- a/platform/platform-resources/src/META-INF/XmlPlugin.xml
+++ b/platform/platform-resources/src/META-INF/XmlPlugin.xml
@@ -449,6 +449,7 @@
+
diff --git a/xml/impl/src/com/intellij/application/options/XmlCodeStyleMainPanel.java b/xml/impl/src/com/intellij/application/options/XmlCodeStyleMainPanel.java
index e465fb04741a..6771e128506c 100644
--- a/xml/impl/src/com/intellij/application/options/XmlCodeStyleMainPanel.java
+++ b/xml/impl/src/com/intellij/application/options/XmlCodeStyleMainPanel.java
@@ -19,6 +19,7 @@ import com.intellij.lang.xml.XMLLanguage;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsProvider;
+import com.intellij.xml.arrangement.XmlArrangementPanel;
/**
* @author Rustam Vishnyakov
@@ -32,6 +33,7 @@ public class XmlCodeStyleMainPanel extends TabbedLanguageCodeStylePanel {
protected void initTabs(CodeStyleSettings settings) {
addIndentOptionsTab(settings);
addTab(new CodeStyleXmlPanel(settings));
+ addTab(new XmlArrangementPanel(settings));
for (CodeStyleSettingsProvider provider : Extensions.getExtensions(CodeStyleSettingsProvider.EXTENSION_POINT_NAME)) {
if (provider.getLanguage() == XMLLanguage.INSTANCE && !provider.hasSettingsPage()) {
diff --git a/xml/impl/src/com/intellij/xml/arrangement/XmlArrangementPanel.java b/xml/impl/src/com/intellij/xml/arrangement/XmlArrangementPanel.java
new file mode 100644
index 000000000000..1b3c0ae6f3b9
--- /dev/null
+++ b/xml/impl/src/com/intellij/xml/arrangement/XmlArrangementPanel.java
@@ -0,0 +1,33 @@
+package com.intellij.xml.arrangement;
+
+import com.intellij.application.options.codeStyle.arrangement.ArrangementSettingsPanel;
+import com.intellij.ide.highlighter.XmlFileType;
+import com.intellij.lang.xml.XMLLanguage;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.psi.codeStyle.CodeStyleSettings;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Eugene.Kudelevsky
+ */
+public class XmlArrangementPanel extends ArrangementSettingsPanel {
+ public XmlArrangementPanel(@NotNull CodeStyleSettings settings) {
+ super(settings, XMLLanguage.INSTANCE);
+ }
+
+ @Override
+ protected int getRightMargin() {
+ return 80;
+ }
+
+ @NotNull
+ @Override
+ protected FileType getFileType() {
+ return XmlFileType.INSTANCE;
+ }
+
+ @Override
+ protected String getPreviewText() {
+ return null;
+ }
+}
diff --git a/xml/impl/src/com/intellij/xml/arrangement/XmlArrangementParseInfo.java b/xml/impl/src/com/intellij/xml/arrangement/XmlArrangementParseInfo.java
new file mode 100644
index 000000000000..8b5261b6e398
--- /dev/null
+++ b/xml/impl/src/com/intellij/xml/arrangement/XmlArrangementParseInfo.java
@@ -0,0 +1,23 @@
+package com.intellij.xml.arrangement;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Eugene.Kudelevsky
+ */
+public class XmlArrangementParseInfo {
+
+ private final List myEntries = new ArrayList();
+
+ @NotNull
+ public List getEntries() {
+ return myEntries;
+ }
+
+ public void addEntry(@NotNull XmlElementArrangementEntry entry) {
+ myEntries.add(entry);
+ }
+}
diff --git a/xml/impl/src/com/intellij/xml/arrangement/XmlArrangementVisitor.java b/xml/impl/src/com/intellij/xml/arrangement/XmlArrangementVisitor.java
new file mode 100644
index 000000000000..e455843ed7db
--- /dev/null
+++ b/xml/impl/src/com/intellij/xml/arrangement/XmlArrangementVisitor.java
@@ -0,0 +1,103 @@
+package com.intellij.xml.arrangement;
+
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.XmlElementVisitor;
+import com.intellij.psi.codeStyle.arrangement.DefaultArrangementEntry;
+import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType;
+import com.intellij.psi.xml.XmlAttribute;
+import com.intellij.psi.xml.XmlFile;
+import com.intellij.psi.xml.XmlTag;
+import com.intellij.util.containers.Stack;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+
+/**
+ * @author Eugene.Kudelevsky
+ */
+public class XmlArrangementVisitor extends XmlElementVisitor {
+
+ private final Stack myStack = new Stack();
+
+ private final XmlArrangementParseInfo myInfo;
+ private final Collection myRanges;
+
+ public XmlArrangementVisitor(@NotNull XmlArrangementParseInfo info, @NotNull Collection ranges) {
+ myInfo = info;
+ myRanges = ranges;
+ }
+
+ @Override
+ public void visitXmlFile(XmlFile file) {
+ final XmlTag tag = file.getRootTag();
+
+ if (tag != null) {
+ tag.accept(this);
+ }
+ }
+
+ @Override
+ public void visitXmlTag(XmlTag tag) {
+ final XmlElementArrangementEntry entry = createNewEntry(
+ tag.getTextRange(), ArrangementEntryType.XML_TAG, null, true);
+ processEntry(entry, tag);
+ }
+
+ @Override
+ public void visitXmlAttribute(XmlAttribute attribute) {
+ final String name = attribute.isNamespaceDeclaration() ? "" : attribute.getName();
+ final XmlElementArrangementEntry entry = createNewEntry(
+ attribute.getTextRange(), ArrangementEntryType.XML_ATTRIBUTE, name, true);
+ processEntry(entry, null);
+ }
+
+ private void processEntry(@Nullable XmlElementArrangementEntry entry, @Nullable PsiElement nextElement) {
+ if (entry == null || nextElement == null) {
+ return;
+ }
+ myStack.push(entry);
+ try {
+ nextElement.acceptChildren(this);
+ }
+ finally {
+ myStack.pop();
+ }
+ }
+
+ @Nullable
+ private XmlElementArrangementEntry createNewEntry(@NotNull TextRange range,
+ @NotNull ArrangementEntryType type,
+ @Nullable String name,
+ boolean canBeMatched) {
+ if (!isWithinBounds(range)) {
+ return null;
+ }
+ final DefaultArrangementEntry current = getCurrent();
+ final XmlElementArrangementEntry entry = new XmlElementArrangementEntry(
+ current, range, type, name, canBeMatched);
+
+ if (current == null) {
+ myInfo.addEntry(entry);
+ }
+ else {
+ current.addChild(entry);
+ }
+ return entry;
+ }
+
+ @Nullable
+ private DefaultArrangementEntry getCurrent() {
+ return myStack.isEmpty() ? null : myStack.peek();
+ }
+
+ private boolean isWithinBounds(@NotNull TextRange range) {
+ for (TextRange textRange : myRanges) {
+ if (textRange.intersects(range)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/xml/impl/src/com/intellij/xml/arrangement/XmlElementArrangementEntry.java b/xml/impl/src/com/intellij/xml/arrangement/XmlElementArrangementEntry.java
new file mode 100644
index 000000000000..e315a79fbd8a
--- /dev/null
+++ b/xml/impl/src/com/intellij/xml/arrangement/XmlElementArrangementEntry.java
@@ -0,0 +1,50 @@
+package com.intellij.xml.arrangement;
+
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.codeStyle.arrangement.ArrangementEntry;
+import com.intellij.psi.codeStyle.arrangement.DefaultArrangementEntry;
+import com.intellij.psi.codeStyle.arrangement.NameAwareArrangementEntry;
+import com.intellij.psi.codeStyle.arrangement.TypeAwareArrangementEntry;
+import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * @author Eugene.Kudelevsky
+ */
+public class XmlElementArrangementEntry extends DefaultArrangementEntry
+ implements TypeAwareArrangementEntry, NameAwareArrangementEntry {
+
+ private final ArrangementEntryType myType;
+ private final String myName;
+
+ public XmlElementArrangementEntry(@Nullable ArrangementEntry parent,
+ @NotNull TextRange range,
+ @NotNull ArrangementEntryType type,
+ @Nullable String name,
+ boolean canBeMatched) {
+ super(parent, range.getStartOffset(), range.getEndOffset(), canBeMatched);
+ myName = name;
+ myType = type;
+ }
+
+ @Nullable
+ @Override
+ public String getName() {
+ return myName;
+ }
+
+ @NotNull
+ @Override
+ public Set getTypes() {
+ return Collections.singleton(myType);
+ }
+
+ @NotNull
+ public ArrangementEntryType getType() {
+ return myType;
+ }
+}
diff --git a/xml/impl/src/com/intellij/xml/arrangement/XmlRearranger.java b/xml/impl/src/com/intellij/xml/arrangement/XmlRearranger.java
new file mode 100644
index 000000000000..a13db532b501
--- /dev/null
+++ b/xml/impl/src/com/intellij/xml/arrangement/XmlRearranger.java
@@ -0,0 +1,182 @@
+package com.intellij.xml.arrangement;
+
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.codeStyle.CodeStyleSettings;
+import com.intellij.psi.codeStyle.arrangement.ArrangementSettings;
+import com.intellij.psi.codeStyle.arrangement.Rearranger;
+import com.intellij.psi.codeStyle.arrangement.StdArrangementSettings;
+import com.intellij.psi.codeStyle.arrangement.group.ArrangementGroupingRule;
+import com.intellij.psi.codeStyle.arrangement.group.ArrangementGroupingType;
+import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType;
+import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier;
+import com.intellij.psi.codeStyle.arrangement.match.StdArrangementMatchRule;
+import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition;
+import com.intellij.psi.codeStyle.arrangement.order.ArrangementEntryOrderType;
+import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsAware;
+import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsRepresentationAware;
+import com.intellij.psi.codeStyle.arrangement.settings.DefaultArrangementSettingsRepresentationManager;
+import gnu.trove.TObjectIntHashMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+
+/**
+ * @author Eugene.Kudelevsky
+ */
+public class XmlRearranger
+ implements Rearranger,
+ ArrangementStandardSettingsAware,
+ ArrangementStandardSettingsRepresentationAware {
+
+ private static final Set SUPPORTED_TYPES = EnumSet.of(
+ ArrangementEntryType.XML_ATTRIBUTE, ArrangementEntryType.XML_TAG);
+
+ private static final List DEFAULT_MATCH_RULES = new ArrayList();
+
+ private static final StdArrangementSettings DEFAULT_SETTINGS = new StdArrangementSettings(
+ Collections.emptyList(), DEFAULT_MATCH_RULES);
+
+ @NotNull private static final TObjectIntHashMap