no more dummy project / problems with formatting when using it
[idea/community.git] / platform / lang-impl / src / com / intellij / psi / formatter / PsiBasedFormattingModel.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package com.intellij.psi.formatter;
18
19 import com.intellij.formatting.Block;
20 import com.intellij.formatting.FormattingDocumentModel;
21 import com.intellij.formatting.FormattingModel;
22 import com.intellij.lang.ASTNode;
23 import com.intellij.openapi.project.Project;
24 import com.intellij.openapi.util.TextRange;
25 import com.intellij.openapi.diagnostic.Logger;
26 import com.intellij.psi.PsiElement;
27 import com.intellij.psi.PsiFile;
28 import com.intellij.psi.TokenType;
29 import com.intellij.psi.PsiDocumentManager;
30 import com.intellij.psi.impl.source.SourceTreeToPsiMap;
31 import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
32 import org.jetbrains.annotations.NotNull;
33 import org.jetbrains.annotations.Nullable;
34
35 public class PsiBasedFormattingModel implements FormattingModel {
36
37   private static final Logger LOG = Logger.getInstance("#com.intellij.psi.formatter.PsiBasedFormattingModel");
38
39   private final ASTNode myASTNode;
40   private final FormattingDocumentModelImpl myDocumentModel;
41   @NotNull private final Block myRootBlock;
42   protected boolean myCanModifyAllWhiteSpaces = false;
43   
44   public PsiBasedFormattingModel(final PsiFile file,
45                                  @NotNull final Block rootBlock,
46                                  final FormattingDocumentModelImpl documentModel) {
47     myASTNode = SourceTreeToPsiMap.psiElementToTree(file);
48     myDocumentModel = documentModel;
49     myRootBlock = rootBlock;
50
51   }
52
53   public TextRange replaceWhiteSpace(TextRange textRange,
54                                 String whiteSpace) {
55     final String wsReplaced = replaceWithPSI(textRange, whiteSpace);
56     
57     if (wsReplaced != null){
58       return new TextRange(textRange.getStartOffset(), textRange.getStartOffset() + wsReplaced.length());
59     } else {
60       return textRange;
61     }
62   }
63
64   public TextRange shiftIndentInsideRange(TextRange textRange, int shift) {
65     return textRange; // TODO: Remove this method from here...
66   }
67
68   public void commitChanges() {
69   }
70
71
72   @Nullable
73   private String replaceWithPSI(final TextRange textRange, String whiteSpace) {
74     final int offset = textRange.getEndOffset();
75     ASTNode leafElement = findElementAt(offset);
76
77     if (leafElement != null) {
78       if (leafElement.getPsi() instanceof PsiFile) {
79         return null;
80       } else {
81         if (!leafElement.getPsi().isValid()) {
82           String message = "Invalid element found in '\n" +
83                            myASTNode.getText() +
84                            "\n' at " +
85                            offset +
86                            "(" +
87                            myASTNode.getText().substring(offset, Math.min(offset + 10, myASTNode.getTextLength()));
88           LOG.error(message);
89         }
90         return replaceWithPsiInLeaf(textRange, whiteSpace, leafElement);
91       }
92     } else if (textRange.getEndOffset() == myASTNode.getTextLength()){
93       FormatterUtil.replaceLastWhiteSpace(myASTNode, whiteSpace, textRange);
94       return whiteSpace;
95     } else {
96       return null;
97     }
98   }
99
100   @Nullable
101   protected String replaceWithPsiInLeaf(final TextRange textRange, String whiteSpace, ASTNode leafElement) {
102     if (!myCanModifyAllWhiteSpaces) {
103       if (leafElement.getElementType() == TokenType.WHITE_SPACE) return null;
104     }
105
106     FormatterUtil.replaceWhiteSpace(whiteSpace, leafElement, TokenType.WHITE_SPACE, textRange);
107     return whiteSpace;
108   }
109
110   @Nullable
111   protected ASTNode findElementAt(final int offset) {
112     PsiFile containingFile = myASTNode.getPsi().getContainingFile();
113     Project project = containingFile.getProject();
114     assert !PsiDocumentManager.getInstance(project).isUncommited(myDocumentModel.getDocument());
115     // TODO:default project can not be used for injections, because latter might wants (unavailable) indices
116     PsiElement psiElement = project.isDefault() ? null :InjectedLanguageUtil.findInjectedElementNoCommitWithOffset(containingFile, offset);
117     if (psiElement == null) psiElement = containingFile.findElementAt(offset);
118     if (psiElement == null) return null;
119     return psiElement.getNode();
120   }
121
122   @NotNull
123   public FormattingDocumentModel getDocumentModel() {
124     return myDocumentModel;
125   }
126
127   @NotNull
128   public Block getRootBlock() {
129     return myRootBlock;
130   }
131
132   public void canModifyAllWhiteSpaces() {
133     myCanModifyAllWhiteSpaces = true;
134   }
135 }