e61a804d10b8ae5b9476c49b06545fea46d14859
[idea/community.git] / platform / lang-impl / src / com / intellij / psi / impl / source / codeStyle / CodeFormatterFacade.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.impl.source.codeStyle;
18
19 import com.intellij.formatting.FormatTextRanges;
20 import com.intellij.formatting.FormatterEx;
21 import com.intellij.formatting.FormattingModel;
22 import com.intellij.formatting.FormattingModelBuilder;
23 import com.intellij.injected.editor.DocumentWindow;
24 import com.intellij.lang.ASTNode;
25 import com.intellij.lang.LanguageFormatting;
26 import com.intellij.openapi.diagnostic.Logger;
27 import com.intellij.openapi.editor.Document;
28 import com.intellij.openapi.editor.RangeMarker;
29 import com.intellij.openapi.extensions.Extensions;
30 import com.intellij.openapi.project.Project;
31 import com.intellij.openapi.util.TextRange;
32 import com.intellij.psi.PsiDocumentManager;
33 import com.intellij.psi.PsiElement;
34 import com.intellij.psi.PsiFile;
35 import com.intellij.psi.codeStyle.CodeStyleSettings;
36 import com.intellij.psi.formatter.DocumentBasedFormattingModel;
37 import com.intellij.psi.impl.source.PostprocessReformattingAspect;
38 import com.intellij.psi.impl.source.SourceTreeToPsiMap;
39 import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
40 import com.intellij.psi.util.PsiTreeUtil;
41 import com.intellij.util.IncorrectOperationException;
42
43 public class CodeFormatterFacade {
44   private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.codeStyle.CodeFormatterFacade");
45
46   private final CodeStyleSettings mySettings;
47
48   public CodeFormatterFacade(CodeStyleSettings settings) {
49     mySettings = settings;
50   }
51
52   public ASTNode processElement(ASTNode element) {
53     TextRange range = element.getTextRange();
54     return processRange(element, range.getStartOffset(), range.getEndOffset());
55   }
56
57   public ASTNode processRange(final ASTNode element, final int startOffset, final int endOffset) {
58     final PsiElement psiElement = SourceTreeToPsiMap.treeElementToPsi(element);
59     final PsiFile file = psiElement.getContainingFile();
60     final Document document = file.getViewProvider().getDocument();
61     final RangeMarker rangeMarker = document != null && endOffset < document.getTextLength()? document.createRangeMarker(startOffset, endOffset):null;
62
63     PsiElement elementToFormat = document instanceof DocumentWindow ? InjectedLanguageUtil.getTopLevelFile(file) : psiElement;
64     final PsiFile fileToFormat = elementToFormat.getContainingFile();
65
66     final FormattingModelBuilder builder = LanguageFormatting.INSTANCE.forContext(fileToFormat);
67     if (builder != null) {
68       TextRange range = preprocess(element, startOffset, endOffset);
69       if (document instanceof DocumentWindow) {
70         DocumentWindow documentWindow = (DocumentWindow)document;
71         range = documentWindow.injectedToHost(range);
72       }
73
74       //final SmartPsiElementPointer pointer = SmartPointerManager.getInstance(psiElement.getProject()).createSmartPsiElementPointer(psiElement);
75       final FormattingModel model = builder.createModel(elementToFormat, mySettings);
76       if (file.getTextLength() > 0) {
77         try {
78           FormatterEx.getInstanceEx().format(model, mySettings,
79                                              mySettings.getIndentOptions(fileToFormat.getFileType()), new FormatTextRanges(range, true));
80         }
81         catch (IncorrectOperationException e) {
82           LOG.error(e);
83         }
84       }
85
86       if (!psiElement.isValid()) {
87         if (rangeMarker != null) {
88           final PsiElement at = file.findElementAt(rangeMarker.getStartOffset());
89           final PsiElement result = PsiTreeUtil.getParentOfType(at, psiElement.getClass(), false);
90           assert result != null;
91           return result.getNode();
92         } else {
93           assert false;
94         }
95       }
96
97 //      return SourceTreeToPsiMap.psiElementToTree(pointer.getElement());
98
99     }
100
101     return element;
102   }
103
104   public void processText(PsiFile file, final FormatTextRanges ranges, boolean doPostponedFormatting) {
105     Project project = file.getProject();
106     Document document = PsiDocumentManager.getInstance(project).getDocument(file);
107     if (document instanceof DocumentWindow) {
108       file = InjectedLanguageUtil.getTopLevelFile(file);
109       final DocumentWindow documentWindow = (DocumentWindow)document;
110       for (FormatTextRanges.FormatTextRange range : ranges.getRanges()) {
111         range.setTextRange(documentWindow.injectedToHost(range.getTextRange()));
112       }
113       document = documentWindow.getDelegate();
114     }
115
116
117     final FormattingModelBuilder builder = LanguageFormatting.INSTANCE.forContext(file);
118
119     if (builder != null) {
120       if (file.getTextLength() > 0) {
121         try {
122           ranges.preprocess(file.getNode());
123           if (doPostponedFormatting) {
124             RangeMarker[] markers = new RangeMarker[ranges.getRanges().size()];
125             int i = 0;
126             for (FormatTextRanges.FormatTextRange range : ranges.getRanges()) {
127               TextRange textRange = range.getTextRange();
128               int start = textRange.getStartOffset();
129               int end = textRange.getEndOffset();
130               if (start >= 0 && end > start && end <= document.getTextLength()) {
131                 markers[i] = document.createRangeMarker(textRange);
132                 markers[i].setGreedyToLeft(true);
133                 markers[i].setGreedyToRight(true);
134                 i++;
135               }
136             }
137             final PostprocessReformattingAspect component = file.getProject().getComponent(PostprocessReformattingAspect.class);
138             component.doPostponedFormatting(file.getViewProvider());
139             i = 0;
140             for (FormatTextRanges.FormatTextRange range : ranges.getRanges()) {
141               if (markers[i] != null) {
142                 range.setTextRange(new TextRange(markers[i].getStartOffset(), markers[i].getEndOffset()));
143               }
144               i++;
145             }
146           }
147           final FormattingModel originalModel = builder.createModel(file, mySettings);
148           final FormattingModel model = new DocumentBasedFormattingModel(originalModel.getRootBlock(),
149                                                                          document,
150                                                                          project, mySettings, file.getFileType(), file);
151
152           FormatterEx.getInstanceEx().format(model, mySettings, mySettings.getIndentOptions(file.getFileType()), ranges);
153         }
154         catch (IncorrectOperationException e) {
155           LOG.error(e);
156         }
157       }
158     }
159   }
160
161   private static TextRange preprocess(final ASTNode node, final int startOffset, final int endOffset) {
162     TextRange result = new TextRange(startOffset, endOffset);
163     for(PreFormatProcessor processor: Extensions.getExtensions(PreFormatProcessor.EP_NAME)) {
164       result = processor.process(node, result);
165     }
166     return result;
167   }
168 }
169