diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettings.java b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettings.java index a3a4de778af6..c6c2b7572eac 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettings.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettings.java @@ -1095,6 +1095,11 @@ public class CodeStyleSettings implements Cloneable, JDOMExternalizable { // ---------------------------------- XML formatting options ------------------------- + + public final static int WS_AROUND_CDATA_PRESERVE = 0; + public final static int WS_AROUND_CDATA_NONE = 1; + public final static int WS_AROUND_CDATA_NEW_LINES = 2; + public boolean XML_KEEP_WHITESPACES = false; public int XML_ATTRIBUTE_WRAP = WRAP_AS_NEEDED; public int XML_TEXT_WRAP = WRAP_AS_NEEDED; @@ -1110,6 +1115,9 @@ public class CodeStyleSettings implements Cloneable, JDOMExternalizable { public boolean XML_SPACE_AFTER_TAG_NAME = false; public boolean XML_SPACE_INSIDE_EMPTY_TAG = false; + public boolean XML_KEEP_WHITE_SPACES_INSIDE_CDATA = false; + public int XML_WHITE_SPACE_AROUND_CDATA = WS_AROUND_CDATA_PRESERVE; + // --------------------------------------------------------------------------------------- // ---------------------------------- HTML formatting options ------------------------- diff --git a/xml/impl/src/com/intellij/psi/formatter/xml/AbstractSyntheticBlock.java b/xml/impl/src/com/intellij/psi/formatter/xml/AbstractSyntheticBlock.java index 5bb57c207c35..0eb42a2f0fc0 100644 --- a/xml/impl/src/com/intellij/psi/formatter/xml/AbstractSyntheticBlock.java +++ b/xml/impl/src/com/intellij/psi/formatter/xml/AbstractSyntheticBlock.java @@ -198,6 +198,11 @@ public abstract class AbstractSyntheticBlock implements Block { return node != null && node.getElementType() == XmlElementType.XML_CDATA; } + public boolean containsCDATA() { + return myStartTreeNode.getElementType() == XmlElementType.XML_CDATA_START && + myEndTreeNode.getElementType() == XmlElementType.XML_CDATA_END; + } + public boolean endsWithCDATA() { return isCDATA(myStartTreeNode.getLastChildNode()); } diff --git a/xml/impl/src/com/intellij/psi/formatter/xml/AbstractXmlBlock.java b/xml/impl/src/com/intellij/psi/formatter/xml/AbstractXmlBlock.java index e21f7f74d3c2..fd0f52fd503f 100644 --- a/xml/impl/src/com/intellij/psi/formatter/xml/AbstractXmlBlock.java +++ b/xml/impl/src/com/intellij/psi/formatter/xml/AbstractXmlBlock.java @@ -414,4 +414,13 @@ public abstract class AbstractXmlBlock extends AbstractBlock { return false; } + + public boolean isCDATAStart() { + return myNode.getElementType() == XmlElementType.XML_CDATA_START; + } + + public boolean isCDATAEnd() { + return myNode.getElementType() == XmlElementType.XML_CDATA_END; + } + } 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 f3e35acaff1d..be96c21df13d 100644 --- a/xml/impl/src/com/intellij/psi/formatter/xml/HtmlPolicy.java +++ b/xml/impl/src/com/intellij/psi/formatter/xml/HtmlPolicy.java @@ -211,6 +211,16 @@ public class HtmlPolicy extends XmlFormattingPolicy { return mySettings.HTML_KEEP_LINE_BREAKS_IN_TEXT; } + @Override + public boolean getKeepWhiteSpacesInsideCDATA() { + return true; + } + + @Override + public int getWhiteSpaceAroundCDATAOption() { + return CodeStyleSettings.WS_AROUND_CDATA_PRESERVE; + } + public CodeStyleSettings getSettings() { return mySettings; } 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 af5056555479..b4f878dd815b 100644 --- a/xml/impl/src/com/intellij/psi/formatter/xml/SyntheticBlock.java +++ b/xml/impl/src/com/intellij/psi/formatter/xml/SyntheticBlock.java @@ -62,6 +62,19 @@ public class SyntheticBlock extends AbstractSyntheticBlock implements Block, Rea boolean firstIsText = isTextFragment(node1); boolean secondIsText = isTextFragment(node2); + if (type1 == XmlElementType.XML_CDATA_START || type2 == XmlElementType.XML_CDATA_END) { + if (myXmlFormattingPolicy.getKeepWhiteSpacesInsideCDATA()) { + return Spacing.getReadOnlySpacing(); + } + if (type1 == XmlElementType.XML_CDATA_START && type2 == XmlElementType.XML_CDATA_END) { + return Spacing.createSpacing(0, 0, 0, myXmlFormattingPolicy.getShouldKeepLineBreaks(), 0); + } + if (type1 == XmlElementType.XML_CDATA_START && child2 instanceof AnotherLanguageBlockWrapper || + type2 == XmlElementType.XML_CDATA_END && child1 instanceof AnotherLanguageBlockWrapper) { + return Spacing.createSpacing(0, 0, 1, myXmlFormattingPolicy.getShouldKeepLineBreaks(), 0); + } + } + boolean firstIsTag = node1.getPsi() instanceof XmlTag && !firstIsText; boolean secondIsTag = node2.getPsi() instanceof XmlTag && !secondIsText; diff --git a/xml/impl/src/com/intellij/psi/formatter/xml/XmlBlock.java b/xml/impl/src/com/intellij/psi/formatter/xml/XmlBlock.java index 88701d97e4ea..1686c7f53c12 100644 --- a/xml/impl/src/com/intellij/psi/formatter/xml/XmlBlock.java +++ b/xml/impl/src/com/intellij/psi/formatter/xml/XmlBlock.java @@ -56,7 +56,7 @@ public class XmlBlock extends AbstractXmlBlock { @NotNull public TextRange getTextRange() { - if (myTextRange != null) { + if (myTextRange != null && !(isCDATAStart() || isCDATAEnd())) { return myTextRange; } else { 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 c12815d7fa49..44ba07f43aff 100644 --- a/xml/impl/src/com/intellij/psi/formatter/xml/XmlFormattingPolicy.java +++ b/xml/impl/src/com/intellij/psi/formatter/xml/XmlFormattingPolicy.java @@ -115,6 +115,10 @@ public abstract class XmlFormattingPolicy { public abstract boolean getShouldKeepLineBreaksInText(); + public abstract boolean getKeepWhiteSpacesInsideCDATA(); + + public abstract int getWhiteSpaceAroundCDATAOption(); + public abstract CodeStyleSettings getSettings(); public boolean processJsp() { 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 aa74224778be..0ee2bf668eb6 100644 --- a/xml/impl/src/com/intellij/psi/formatter/xml/XmlPolicy.java +++ b/xml/impl/src/com/intellij/psi/formatter/xml/XmlPolicy.java @@ -102,6 +102,16 @@ public class XmlPolicy extends XmlFormattingPolicy{ return mySettings.XML_KEEP_LINE_BREAKS_IN_TEXT; } + @Override + public boolean getKeepWhiteSpacesInsideCDATA() { + return mySettings.XML_KEEP_WHITE_SPACES_INSIDE_CDATA; + } + + @Override + public int getWhiteSpaceAroundCDATAOption() { + return mySettings.XML_WHITE_SPACE_AROUND_CDATA; + } + public CodeStyleSettings getSettings() { return mySettings; } 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 9370145ae91b..6ece1da7a721 100644 --- a/xml/impl/src/com/intellij/psi/formatter/xml/XmlTagBlock.java +++ b/xml/impl/src/com/intellij/psi/formatter/xml/XmlTagBlock.java @@ -18,6 +18,7 @@ package com.intellij.psi.formatter.xml; import com.intellij.formatting.*; 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.tree.IElementType; import com.intellij.psi.xml.XmlElementType; @@ -206,6 +207,24 @@ public class XmlTagBlock extends AbstractXmlBlock{ return Spacing.getReadOnlySpacing(); } + if (syntheticBlock1.containsCDATA() && syntheticBlock2.isTagDescription() + || syntheticBlock1.isTagDescription() && syntheticBlock2.containsCDATA()) { + int lineFeeds = 0; + switch(myXmlFormattingPolicy.getWhiteSpaceAroundCDATAOption()) { + case CodeStyleSettings.WS_AROUND_CDATA_NONE: + break; + case CodeStyleSettings.WS_AROUND_CDATA_NEW_LINES: + lineFeeds = 1; + break; + case CodeStyleSettings.WS_AROUND_CDATA_PRESERVE: + return Spacing.getReadOnlySpacing(); + default: + assert false : "Unexpected whitespace around CDATA code style option."; + } + return Spacing.createSpacing(0, 0, lineFeeds, myXmlFormattingPolicy.getShouldKeepLineBreaks(), + myXmlFormattingPolicy.getKeepBlankLines()); + } + if (syntheticBlock2.isJspTextBlock() || syntheticBlock1.isJspTextBlock()) { return Spacing.createSafeSpacing(myXmlFormattingPolicy.getShouldKeepLineBreaks(), myXmlFormattingPolicy.getKeepBlankLines()); } diff --git a/xml/impl/src/com/intellij/psi/impl/source/xml/XmlTextImpl.java b/xml/impl/src/com/intellij/psi/impl/source/xml/XmlTextImpl.java index ff759239a353..fb627d5bf548 100644 --- a/xml/impl/src/com/intellij/psi/impl/source/xml/XmlTextImpl.java +++ b/xml/impl/src/com/intellij/psi/impl/source/xml/XmlTextImpl.java @@ -366,15 +366,23 @@ public class XmlTextImpl extends XmlElementImpl implements XmlText, PsiLanguageI public TextRange getCDATAInterior() { PsiElement[] elements = getChildren(); int start = 0; - if (elements.length != 0 && elements[0].getNode().getElementType() == XmlElementType.XML_CDATA) { - ASTNode startNode = elements[0].getNode().findChildByType(XmlTokenType.XML_CDATA_START); + int first = 0; + if (elements.length > 0 && elements[0] instanceof PsiWhiteSpace) { + first ++; + } + if (elements.length > first && elements[first].getNode().getElementType() == XmlElementType.XML_CDATA) { + ASTNode startNode = elements[first].getNode().findChildByType(XmlTokenType.XML_CDATA_START); if (startNode != null) { start = startNode.getTextRange().getEndOffset() - getTextRange().getStartOffset(); } } int end = getTextLength(); - if (elements.length != 0 && elements[elements.length-1].getNode().getElementType() == XmlElementType.XML_CDATA) { - ASTNode startNode = elements[elements.length-1].getNode().findChildByType(XmlTokenType.XML_CDATA_END); + int last = elements.length - 1; + if (last > 0 && elements[last] instanceof PsiWhiteSpace) { + last --; + } + if (last >= 0 && elements[last].getNode().getElementType() == XmlElementType.XML_CDATA) { + ASTNode startNode = elements[last].getNode().findChildByType(XmlTokenType.XML_CDATA_END); if (startNode != null) { end = startNode.getTextRange().getStartOffset() - getTextRange().getStartOffset(); }