2 * Copyright 2000-2012 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.
17 package com.intellij.psi.formatter;
19 import com.intellij.formatting.Block;
20 import com.intellij.formatting.FormattingDocumentModel;
21 import com.intellij.formatting.FormattingModelEx;
22 import com.intellij.lang.ASTNode;
23 import com.intellij.openapi.diagnostic.Logger;
24 import com.intellij.openapi.project.Project;
25 import com.intellij.openapi.util.TextRange;
26 import com.intellij.psi.PsiDocumentManager;
27 import com.intellij.psi.PsiElement;
28 import com.intellij.psi.PsiFile;
29 import com.intellij.psi.TokenType;
30 import com.intellij.psi.codeStyle.CodeStyleManager;
31 import com.intellij.psi.impl.source.SourceTreeToPsiMap;
32 import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
33 import org.jetbrains.annotations.NotNull;
34 import org.jetbrains.annotations.Nullable;
36 public class PsiBasedFormattingModel implements FormattingModelEx {
38 private static final Logger LOG = Logger.getInstance("#com.intellij.psi.formatter.PsiBasedFormattingModel");
40 private final Project myProject;
41 private final ASTNode myASTNode;
42 private final FormattingDocumentModelImpl myDocumentModel;
43 @NotNull private final Block myRootBlock;
44 protected boolean myCanModifyAllWhiteSpaces = false;
46 public PsiBasedFormattingModel(final PsiFile file,
47 @NotNull final Block rootBlock,
48 final FormattingDocumentModelImpl documentModel) {
49 myASTNode = SourceTreeToPsiMap.psiElementToTree(file);
50 myDocumentModel = documentModel;
51 myRootBlock = rootBlock;
52 myProject = file.getProject();
58 public TextRange replaceWhiteSpace(TextRange textRange, String whiteSpace) {
59 return replaceWhiteSpace(textRange, null, whiteSpace);
63 public TextRange replaceWhiteSpace(TextRange textRange, ASTNode nodeAfter, String whiteSpace) {
64 String whiteSpaceToUse
65 = myDocumentModel.adjustWhiteSpaceIfNecessary(whiteSpace, textRange.getStartOffset(), textRange.getEndOffset(), nodeAfter, true).toString();
66 final String wsReplaced = replaceWithPSI(textRange, whiteSpaceToUse);
68 if (wsReplaced != null){
69 return new TextRange(textRange.getStartOffset(), textRange.getStartOffset() + wsReplaced.length());
76 public TextRange shiftIndentInsideRange(ASTNode node, TextRange textRange, int shift) {
77 return textRange; // TODO: Remove this method from here...
81 public void commitChanges() {
86 private String replaceWithPSI(final TextRange textRange, final String whiteSpace) {
87 final int offset = textRange.getEndOffset();
88 ASTNode leafElement = findElementAt(offset);
90 if (leafElement != null) {
91 if (leafElement.getPsi() instanceof PsiFile) {
94 if (!leafElement.getPsi().isValid()) {
95 String message = "Invalid element found in '\n" +
100 myASTNode.getText().substring(offset, Math.min(offset + 10, myASTNode.getTextLength()));
103 return replaceWithPsiInLeaf(textRange, whiteSpace, leafElement);
105 } else if (textRange.getEndOffset() == myASTNode.getTextLength()){
107 CodeStyleManager.getInstance(myProject).performActionWithFormatterDisabled(new Runnable() {
110 FormatterUtil.replaceLastWhiteSpace(myASTNode, whiteSpace, textRange);
121 protected String replaceWithPsiInLeaf(final TextRange textRange, final String whiteSpace, final ASTNode leafElement) {
122 if (!myCanModifyAllWhiteSpaces) {
123 if (leafElement.getElementType() == TokenType.WHITE_SPACE) return null;
126 CodeStyleManager.getInstance(myProject).performActionWithFormatterDisabled(new Runnable() {
129 FormatterUtil.replaceWhiteSpace(whiteSpace, leafElement, TokenType.WHITE_SPACE, textRange);
137 protected ASTNode findElementAt(final int offset) {
138 PsiFile containingFile = myASTNode.getPsi().getContainingFile();
139 Project project = containingFile.getProject();
140 assert !PsiDocumentManager.getInstance(project).isUncommited(myDocumentModel.getDocument());
141 // TODO:default project can not be used for injections, because latter might wants (unavailable) indices
142 PsiElement psiElement = project.isDefault() ? null : InjectedLanguageUtil.findInjectedElementNoCommit(containingFile, offset);
143 if (psiElement == null) psiElement = containingFile.findElementAt(offset);
144 if (psiElement == null) return null;
145 return psiElement.getNode();
150 public FormattingDocumentModel getDocumentModel() {
151 return myDocumentModel;
156 public Block getRootBlock() {
160 public void canModifyAllWhiteSpaces() {
161 myCanModifyAllWhiteSpaces = true;