2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
20 package com
.intellij
.lang
.xml
;
22 import com
.intellij
.formatting
.Block
;
23 import com
.intellij
.lang
.ASTNode
;
24 import com
.intellij
.openapi
.diagnostic
.Logger
;
25 import com
.intellij
.openapi
.project
.Project
;
26 import com
.intellij
.openapi
.util
.TextRange
;
27 import com
.intellij
.psi
.PsiFile
;
28 import com
.intellij
.psi
.PsiWhiteSpace
;
29 import com
.intellij
.psi
.TokenType
;
30 import com
.intellij
.psi
.formatter
.FormatterUtil
;
31 import com
.intellij
.psi
.formatter
.FormattingDocumentModelImpl
;
32 import com
.intellij
.psi
.formatter
.PsiBasedFormattingModel
;
33 import com
.intellij
.psi
.impl
.source
.tree
.TreeUtil
;
34 import com
.intellij
.psi
.tree
.IElementType
;
35 import com
.intellij
.psi
.xml
.XmlElementType
;
36 import org
.jetbrains
.annotations
.NonNls
;
37 import org
.jetbrains
.annotations
.Nullable
;
39 public class XmlFormattingModel
extends PsiBasedFormattingModel
{
40 private static final Logger LOG
=
41 Logger
.getInstance("#com.intellij.psi.impl.source.codeStyle.PsiBasedFormatterModelWithShiftIndentInside");
43 private final Project myProject
;
45 public XmlFormattingModel(final PsiFile file
,
46 final Block rootBlock
,
47 final FormattingDocumentModelImpl documentModel
) {
48 super(file
, rootBlock
, documentModel
);
49 myProject
= file
.getProject();
52 public TextRange
shiftIndentInsideRange(TextRange textRange
, int shift
) {
53 return shiftIndentInsideWithPsi(textRange
, shift
);
56 public void commitChanges() {
60 private TextRange
shiftIndentInsideWithPsi(final TextRange textRange
, final int shift
) {
61 final int offset
= textRange
.getStartOffset();
63 ASTNode leafElement
= findElementAt(offset
);
64 while (leafElement
!= null && !leafElement
.getTextRange().equals(textRange
)) {
65 leafElement
= leafElement
.getTreeParent();
71 protected String
replaceWithPsiInLeaf(final TextRange textRange
, String whiteSpace
, ASTNode leafElement
) {
72 if (!myCanModifyAllWhiteSpaces
) {
73 if (leafElement
.getElementType() == TokenType
.WHITE_SPACE
) return null;
74 LOG
.assertTrue(leafElement
.getPsi().isValid());
75 ASTNode prevNode
= TreeUtil
.prevLeaf(leafElement
);
77 if (prevNode
!= null) {
78 IElementType type
= prevNode
.getElementType();
79 if(type
== TokenType
.WHITE_SPACE
) {
80 final String text
= prevNode
.getText();
82 final @NonNls String cdataStartMarker
= "<![CDATA[";
83 final int cdataPos
= text
.indexOf(cdataStartMarker
);
84 if (cdataPos
!= -1 && whiteSpace
.indexOf(cdataStartMarker
) == -1) {
85 whiteSpace
= mergeWsWithCdataMarker(whiteSpace
, text
, cdataPos
);
86 if (whiteSpace
== null) return null;
89 prevNode
= TreeUtil
.prevLeaf(prevNode
);
90 type
= prevNode
!= null ? prevNode
.getElementType():null;
93 final @NonNls String cdataEndMarker
= "]]>";
94 if(type
== XmlElementType
.XML_CDATA_END
&& whiteSpace
.indexOf(cdataEndMarker
) == -1) {
95 final ASTNode at
= findElementAt(prevNode
.getStartOffset());
97 if (at
!= null && at
.getPsi() instanceof PsiWhiteSpace
) {
98 final String s
= at
.getText();
99 final int cdataEndPos
= s
.indexOf(cdataEndMarker
);
100 whiteSpace
= mergeWsWithCdataMarker(whiteSpace
, s
, cdataEndPos
);
105 if (whiteSpace
== null) return null;
109 FormatterUtil
.replaceWhiteSpace(whiteSpace
, leafElement
, TokenType
.WHITE_SPACE
, textRange
);
114 private static String
mergeWsWithCdataMarker(String whiteSpace
, final String s
, final int cdataPos
) {
115 final int firstCrInGeneratedWs
= whiteSpace
.indexOf('\n');
116 final int secondCrInGeneratedWs
= firstCrInGeneratedWs
!= -1 ? whiteSpace
.indexOf('\n', firstCrInGeneratedWs
+ 1):-1;
117 final int firstCrInPreviousWs
= s
.indexOf('\n');
118 final int secondCrInPreviousWs
= firstCrInPreviousWs
!= -1 ? s
.indexOf('\n', firstCrInPreviousWs
+ 1):-1;
120 boolean knowHowToModifyCData
= false;
122 if (secondCrInPreviousWs
!= -1 && secondCrInGeneratedWs
!= -1 && cdataPos
> firstCrInPreviousWs
&& cdataPos
< secondCrInPreviousWs
) {
123 whiteSpace
= whiteSpace
.substring(0, secondCrInGeneratedWs
) + s
.substring(firstCrInPreviousWs
+ 1, secondCrInPreviousWs
) + whiteSpace
.substring(secondCrInGeneratedWs
);
124 knowHowToModifyCData
= true;
126 if (!knowHowToModifyCData
) whiteSpace
= null;