1 package com
.intellij
.psi
.impl
.source
;
3 import com
.intellij
.lang
.ASTNode
;
4 import com
.intellij
.openapi
.Disposable
;
5 import com
.intellij
.openapi
.application
.ApplicationAdapter
;
6 import com
.intellij
.openapi
.application
.ApplicationListener
;
7 import com
.intellij
.openapi
.application
.ApplicationManager
;
8 import com
.intellij
.openapi
.command
.CommandProcessor
;
9 import com
.intellij
.openapi
.diagnostic
.Logger
;
10 import com
.intellij
.openapi
.editor
.Document
;
11 import com
.intellij
.openapi
.editor
.RangeMarker
;
12 import com
.intellij
.openapi
.fileTypes
.FileType
;
13 import com
.intellij
.openapi
.fileTypes
.FileTypeManager
;
14 import com
.intellij
.openapi
.project
.Project
;
15 import com
.intellij
.openapi
.util
.Computable
;
16 import com
.intellij
.openapi
.util
.Disposer
;
17 import com
.intellij
.openapi
.util
.Pair
;
18 import com
.intellij
.openapi
.util
.TextRange
;
19 import com
.intellij
.pom
.PomManager
;
20 import com
.intellij
.pom
.PomModelAspect
;
21 import com
.intellij
.pom
.event
.PomModelEvent
;
22 import com
.intellij
.pom
.tree
.TreeAspect
;
23 import com
.intellij
.pom
.tree
.events
.ChangeInfo
;
24 import com
.intellij
.pom
.tree
.events
.TreeChange
;
25 import com
.intellij
.pom
.tree
.events
.TreeChangeEvent
;
26 import com
.intellij
.psi
.*;
27 import com
.intellij
.psi
.codeStyle
.CodeStyleSettings
;
28 import com
.intellij
.psi
.codeStyle
.CodeStyleSettingsManager
;
29 import com
.intellij
.psi
.impl
.PsiTreeDebugBuilder
;
30 import com
.intellij
.psi
.impl
.source
.codeStyle
.CodeEditUtil
;
31 import com
.intellij
.psi
.impl
.source
.codeStyle
.CodeFormatterFacade
;
32 import com
.intellij
.psi
.impl
.source
.codeStyle
.Helper
;
33 import com
.intellij
.psi
.impl
.source
.codeStyle
.HelperFactory
;
34 import com
.intellij
.psi
.impl
.source
.tree
.*;
35 import com
.intellij
.psi
.impl
.source
.tree
.injected
.InjectedLanguageUtil
;
36 import com
.intellij
.util
.LocalTimeCounter
;
37 import com
.intellij
.util
.text
.CharArrayUtil
;
38 import junit
.framework
.Assert
;
39 import org
.jetbrains
.annotations
.NonNls
;
40 import org
.jetbrains
.annotations
.NotNull
;
44 public class PostprocessReformattingAspect
implements PomModelAspect
, Disposable
{
45 private static final Logger LOG
= Logger
.getInstance("#com.intellij.psi.impl.source.PostprocessReformatingAspect");
46 private final Project myProject
;
47 private final PsiManager myPsiManager
;
48 private final TreeAspect myTreeAspect
;
49 private final Map
<FileViewProvider
, List
<ASTNode
>> myReformatElements
= new HashMap
<FileViewProvider
, List
<ASTNode
>>();
50 private volatile int myDisabledCounter
= 0;
51 private final Set
<FileViewProvider
> myUpdatedProviders
= new HashSet
<FileViewProvider
>();
53 private final ApplicationListener myApplicationListener
= new ApplicationAdapter() {
54 public void writeActionStarted(final Object action
) {
55 final CommandProcessor processor
= CommandProcessor
.getInstance();
56 if (processor
!= null) {
57 final Project project
= processor
.getCurrentCommandProject();
58 if(project
== myProject
) {
64 public void writeActionFinished(final Object action
) {
65 final CommandProcessor processor
= CommandProcessor
.getInstance();
66 if (processor
!= null) {
67 final Project project
= processor
.getCurrentCommandProject();
68 if(project
== myProject
) {
69 decrementPostponedCounter();
75 public PostprocessReformattingAspect(Project project
, PsiManager psiManager
, TreeAspect treeAspect
) {
77 myPsiManager
= psiManager
;
78 myTreeAspect
= treeAspect
;
79 PomManager
.getModel(psiManager
.getProject()).registerAspect(PostprocessReformattingAspect
.class, this, Collections
.singleton((PomModelAspect
)treeAspect
));
81 ApplicationManager
.getApplication().addApplicationListener(myApplicationListener
);
82 Disposer
.register(project
, this);
85 public void dispose() {
86 ApplicationManager
.getApplication().removeApplicationListener(myApplicationListener
);
89 public void disablePostprocessFormattingInside(final Runnable runnable
) {
90 disablePostprocessFormattingInside(new Computable
<Object
>() {
91 public Object
compute() {
98 public <T
> T
disablePostprocessFormattingInside(Computable
<T
> computable
){
101 return computable
.compute();
105 LOG
.assertTrue(myDisabledCounter
> 0 || !isDisabled());
109 private int myPostponedCounter
= 0;
110 public void postponeFormattingInside(final Runnable runnable
) {
111 postponeFormattingInside(new Computable
<Object
>() {
112 public Object
compute() {
119 public <T
> T
postponeFormattingInside(Computable
<T
> computable
){
121 //if(myPostponedCounter == 0) myDisabled = false;
122 myPostponedCounter
++;
123 return computable
.compute();
126 decrementPostponedCounter();
130 private void decrementPostponedCounter() {
131 if (--myPostponedCounter
== 0) {
132 if(!ApplicationManager
.getApplication().isWriteAccessAllowed()){
133 ApplicationManager
.getApplication().runWriteAction(new Runnable() {
135 doPostponedFormatting();
139 else doPostponedFormatting();
144 public void update(PomModelEvent event
) {
145 synchronized(PsiLock
.LOCK
){
146 if(isDisabled() || myPostponedCounter
== 0 && !ApplicationManager
.getApplication().isUnitTestMode()) return;
147 final TreeChangeEvent changeSet
= (TreeChangeEvent
)event
.getChangeSet(myTreeAspect
);
148 if(changeSet
== null) return;
149 final PsiElement psiElement
= changeSet
.getRootElement().getPsi();
150 if(psiElement
== null) return;
151 PsiFile containingFile
= InjectedLanguageUtil
.getTopLevelFile(psiElement
);
152 final FileViewProvider viewProvider
= containingFile
.getViewProvider();
154 if(!viewProvider
.isEventSystemEnabled()) return;
155 myUpdatedProviders
.add(viewProvider
);
156 for (final ASTNode node
: changeSet
.getChangedElements()) {
157 final TreeChange treeChange
= changeSet
.getChangesByElement(node
);
158 for (final ASTNode affectedChild
: treeChange
.getAffectedChildren()) {
159 final ChangeInfo childChange
= treeChange
.getChangeByChild(affectedChild
);
160 switch(childChange
.getChangeType()){
162 case ChangeInfo
.REPLACE
:
163 postponeFormatting(viewProvider
, affectedChild
);
165 case ChangeInfo
.CONTENTS_CHANGED
:
166 if(!CodeEditUtil
.isNodeGenerated(affectedChild
))
167 ((TreeElement
)affectedChild
).acceptTree(new RecursiveTreeElementWalkingVisitor(){
168 protected boolean visitNode(TreeElement element
) {
169 if(CodeEditUtil
.isNodeGenerated(element
)){
170 postponeFormatting(viewProvider
, element
);
183 public void doPostponedFormatting(){
184 synchronized(PsiLock
.LOCK
){
185 if(isDisabled()) return;
187 for (final FileViewProvider viewProvider
: myUpdatedProviders
) {
188 doPostponedFormatting(viewProvider
);
192 LOG
.assertTrue(myReformatElements
.isEmpty());
193 myUpdatedProviders
.clear();
194 myReformatElements
.clear();
199 public void doPostponedFormatting(final FileViewProvider viewProvider
) {
200 synchronized(PsiLock
.LOCK
){
201 if(isDisabled()) return;
203 disablePostprocessFormattingInside(new Runnable() {
205 doPostponedFormattingInner(viewProvider
);
211 public boolean isViewProviderLocked(final FileViewProvider fileViewProvider
) {
212 return myReformatElements
.containsKey(fileViewProvider
);
215 public static PostprocessReformattingAspect
getInstance(Project project
) {
216 return project
.getComponent(PostprocessReformattingAspect
.class);
219 private void postponeFormatting(final FileViewProvider viewProvider
, final ASTNode child
) {
220 if (!CodeEditUtil
.isNodeGenerated(child
) && child
.getElementType() != TokenType
.WHITE_SPACE
) {
221 final int oldIndent
= CodeEditUtil
.getOldIndentation(child
);
222 LOG
.assertTrue(oldIndent
>= 0, "for not generated items old indentation must be defined: element=" + child
+ ", text=" + child
.getText());
224 List
<ASTNode
> list
= myReformatElements
.get(viewProvider
);
226 list
= new ArrayList
<ASTNode
>();
227 myReformatElements
.put(viewProvider
, list
);
232 private void doPostponedFormattingInner(final FileViewProvider key
) {
235 final List
<ASTNode
> astNodes
= myReformatElements
.remove(key
);
236 final Document document
= key
.getDocument();
237 // Sort ranges by end offsets so that we won't need any offset adjustment after reformat or reindent
238 if (document
== null /*|| documentManager.isUncommited(document) TODO */) return;
240 final TreeMap
<RangeMarker
, PostponedAction
> rangesToProcess
= new TreeMap
<RangeMarker
, PostponedAction
>(new Comparator
<RangeMarker
>() {
241 public int compare(final RangeMarker o1
, final RangeMarker o2
) {
242 if (o1
.equals(o2
)) return 0;
243 final int diff
= o2
.getEndOffset() - o1
.getEndOffset();
245 if(o1
.getStartOffset() == o2
.getStartOffset()) return 0;
246 if(o1
.getStartOffset() == o1
.getEndOffset()) return -1; // empty ranges first
247 if(o2
.getStartOffset() == o2
.getEndOffset()) return 1; // empty ranges first
248 return o1
.getStartOffset() - o2
.getStartOffset();
254 // process all roots in viewProvider to find marked for reformat before elements and create appropriate ragge markers
255 handleReformatMarkers(key
, rangesToProcess
);
257 // then we create ranges by changed nodes. One per node. There ranges can instersect. Ranges are sorted by end offset.
258 if (astNodes
!= null) createActionsMap(astNodes
, key
, rangesToProcess
);
260 if ("true".equals(System
.getProperty("check.psi.is.valid")) && ApplicationManager
.getApplication().isUnitTestMode()) {
261 checkPsiIsCorrect(key
);
264 while(!rangesToProcess
.isEmpty()){
265 // now we have to normalize actions so that they not intersect and ordered in most appropriate way
266 // (free reformating -> reindent -> formating under reindent)
267 final List
<Pair
<RangeMarker
, ?
extends PostponedAction
>> normalizedActions
= normalizeAndReorderPostponedActions(rangesToProcess
, document
);
269 // only in following loop real changes in document are made
270 for (final Pair
<RangeMarker
, ?
extends PostponedAction
> normalizedAction
: normalizedActions
) {
271 CodeStyleSettings settings
= CodeStyleSettingsManager
.getSettings(myPsiManager
.getProject());
272 boolean old
= settings
.ENABLE_JAVADOC_FORMATTING
;
273 settings
.ENABLE_JAVADOC_FORMATTING
= false;
275 normalizedAction
.getSecond().processRange(normalizedAction
.getFirst(), key
);
278 settings
.ENABLE_JAVADOC_FORMATTING
= old
;
284 private void checkPsiIsCorrect(final FileViewProvider key
) {
285 PsiFile actualPsi
= key
.getPsi(key
.getBaseLanguage());
287 PsiTreeDebugBuilder treeDebugBuilder
= new PsiTreeDebugBuilder().setShowErrorElements(false).setShowWhiteSpaces(false);
289 String actualPsiTree
= treeDebugBuilder
.psiToString(actualPsi
);
291 String fileName
= key
.getVirtualFile().getName();
292 PsiFile psi
= PsiFileFactory
.getInstance(myProject
)
293 .createFileFromText(fileName
, FileTypeManager
.getInstance().getFileTypeByFileName(fileName
), actualPsi
.getNode().getText(),
294 LocalTimeCounter
.currentTime(), false);
296 if (actualPsi
.getClass().equals(psi
.getClass())) {
297 String expectedPsi
= treeDebugBuilder
.psiToString(psi
);
299 if (!expectedPsi
.equals(actualPsiTree
)) {
300 myReformatElements
.clear();
301 Assert
.assertEquals("Refactored psi should be the same as result of parsing", expectedPsi
, actualPsiTree
);
308 private List
<Pair
<RangeMarker
, ?
extends PostponedAction
>> normalizeAndReorderPostponedActions(final TreeMap
<RangeMarker
, PostponedAction
> rangesToProcess
, Document document
) {
309 final List
<Pair
<RangeMarker
, ReformatAction
>> freeFormatingActions
= new ArrayList
<Pair
<RangeMarker
, ReformatAction
>>();
310 final List
<Pair
<RangeMarker
, ReindentAction
>> indentActions
= new ArrayList
<Pair
<RangeMarker
, ReindentAction
>>();
312 RangeMarker accumulatedRange
= null;
313 PostponedAction accumulatedRangeAction
= null;
314 Iterator
<Map
.Entry
<RangeMarker
, PostponedAction
>> iterator
= rangesToProcess
.entrySet().iterator();
315 while (iterator
.hasNext()) {
316 final Map
.Entry
<RangeMarker
, PostponedAction
> entry
= iterator
.next();
317 final RangeMarker textRange
= entry
.getKey();
318 final PostponedAction action
= entry
.getValue();
319 if (accumulatedRange
== null) {
320 accumulatedRange
= textRange
;
321 accumulatedRangeAction
= action
;
324 else if (accumulatedRange
.getStartOffset() > textRange
.getEndOffset() ||
325 (accumulatedRange
.getStartOffset() == textRange
.getEndOffset() &&
326 !canStickActionsTogether(accumulatedRangeAction
, accumulatedRange
, action
, textRange
))) {
327 // action can be pushed
328 if (accumulatedRangeAction
instanceof ReindentAction
)
329 indentActions
.add(new Pair
<RangeMarker
, ReindentAction
>(accumulatedRange
, (ReindentAction
)accumulatedRangeAction
));
331 freeFormatingActions
.add(new Pair
<RangeMarker
, ReformatAction
>(accumulatedRange
, (ReformatAction
)accumulatedRangeAction
));
333 accumulatedRange
= textRange
;
334 accumulatedRangeAction
= action
;
337 else if (accumulatedRangeAction
instanceof ReformatAction
&& action
instanceof ReindentAction
) {
338 // split accumulated reformat range into two
339 if (accumulatedRange
.getStartOffset() < textRange
.getStartOffset()) {
340 final RangeMarker endOfRange
= document
.createRangeMarker(accumulatedRange
.getStartOffset(), textRange
.getStartOffset());
341 // add heading reformat part
342 rangesToProcess
.put(endOfRange
, accumulatedRangeAction
);
343 // and manage heading whitespace because formatter does not edit it in previous action
344 iterator
= rangesToProcess
.entrySet().iterator();
345 //noinspection StatementWithEmptyBody
346 while(iterator
.next().getKey() != textRange
);
348 final RangeMarker rangeToProcess
= document
.createRangeMarker(textRange
.getEndOffset(), accumulatedRange
.getEndOffset());
349 freeFormatingActions
.add(new Pair
<RangeMarker
, ReformatAction
>(rangeToProcess
, new ReformatWithHeadingWhitespaceAction()));
350 accumulatedRange
= textRange
;
351 accumulatedRangeAction
= action
;
355 if (!(accumulatedRangeAction
instanceof ReindentAction
)) {
357 if(accumulatedRangeAction
instanceof ReformatAction
&& action
instanceof ReformatWithHeadingWhitespaceAction
&&
358 accumulatedRange
.getStartOffset() == textRange
.getStartOffset() ||
359 accumulatedRangeAction
instanceof ReformatWithHeadingWhitespaceAction
&& action
instanceof ReformatAction
&&
360 accumulatedRange
.getStartOffset() < textRange
.getStartOffset()){
361 accumulatedRangeAction
= action
;
363 accumulatedRange
= document
.createRangeMarker(Math
.min(accumulatedRange
.getStartOffset(), textRange
.getStartOffset()),
364 Math
.max(accumulatedRange
.getEndOffset(), textRange
.getEndOffset()));
366 else if(action
instanceof ReindentAction
) iterator
.remove(); // TODO[ik]: need to be fixed to correctly process indent inside indent
369 if (accumulatedRange
!= null){
370 if (accumulatedRangeAction
instanceof ReindentAction
)
371 indentActions
.add(new Pair
<RangeMarker
, ReindentAction
>(accumulatedRange
, (ReindentAction
)accumulatedRangeAction
));
373 freeFormatingActions
.add(new Pair
<RangeMarker
, ReformatAction
>(accumulatedRange
, (ReformatAction
)accumulatedRangeAction
));
376 final List
<Pair
<RangeMarker
, ?
extends PostponedAction
>> result
=
377 new ArrayList
<Pair
<RangeMarker
, ?
extends PostponedAction
>>(rangesToProcess
.size());
378 Collections
.reverse(freeFormatingActions
);
379 Collections
.reverse(indentActions
);
380 result
.addAll(freeFormatingActions
);
381 result
.addAll(indentActions
);
385 private boolean canStickActionsTogether(final PostponedAction currentAction
,
386 final RangeMarker currentRange
,
387 final PostponedAction nextAction
,
388 final RangeMarker nextRange
) {
389 // empty reformat markers can't sticked together with any action
390 if(nextAction
instanceof ReformatWithHeadingWhitespaceAction
&& nextRange
.getStartOffset() == nextRange
.getEndOffset()) return false;
391 if(currentAction
instanceof ReformatWithHeadingWhitespaceAction
&& currentRange
.getStartOffset() == currentRange
.getEndOffset()) return false;
392 // reindent actions can't be sticked at all
393 return !(currentAction
instanceof ReindentAction
);
396 private void createActionsMap(final List
<ASTNode
> astNodes
, final FileViewProvider provider
,
397 final TreeMap
<RangeMarker
, PostponedAction
> rangesToProcess
) {
398 final Set
<ASTNode
> nodesToProcess
= new HashSet
<ASTNode
>(astNodes
);
399 final Document document
= provider
.getDocument();
400 for (final ASTNode node
: astNodes
) {
401 nodesToProcess
.remove(node
);
402 final FileElement fileElement
= TreeUtil
.getFileElement((TreeElement
)node
);
403 if (fileElement
== null || ((PsiFile
)fileElement
.getPsi()).getViewProvider() != provider
) continue;
404 final boolean isGenerated
= CodeEditUtil
.isNodeGenerated(node
);
406 ((TreeElement
)node
).acceptTree(new RecursiveTreeElementVisitor() {
407 boolean inGeneratedContext
= !isGenerated
;
408 protected boolean visitNode(TreeElement element
) {
409 if(nodesToProcess
.contains(element
)) return false;
410 final boolean currentNodeGenerated
= CodeEditUtil
.isNodeGenerated(element
);
411 CodeEditUtil
.setNodeGenerated(element
, false);
412 if(currentNodeGenerated
&& !inGeneratedContext
){
413 rangesToProcess
.put(document
.createRangeMarker(element
.getTextRange()), new ReformatAction());
414 inGeneratedContext
= true;
416 if(!currentNodeGenerated
&& inGeneratedContext
){
417 if(element
.getElementType() == TokenType
.WHITE_SPACE
) return false;
418 final int oldIndent
= CodeEditUtil
.getOldIndentation(element
);
419 LOG
.assertTrue(oldIndent
>= 0, "for not generated items old indentation must be defined");
420 rangesToProcess
.put(document
.createRangeMarker(element
.getTextRange()), new ReindentAction(oldIndent
));
421 inGeneratedContext
= false;
426 @Override public void visitComposite(CompositeElement composite
) {
427 boolean oldGeneratedContext
= inGeneratedContext
;
428 super.visitComposite(composite
);
429 inGeneratedContext
= oldGeneratedContext
;
432 @Override public void visitLeaf(LeafElement leaf
) {
433 boolean oldGeneratedContext
= inGeneratedContext
;
434 super.visitLeaf(leaf
);
435 inGeneratedContext
= oldGeneratedContext
;
441 private void handleReformatMarkers(final FileViewProvider key
,
442 final TreeMap
<RangeMarker
, PostponedAction
> rangesToProcess
) {
443 final Document document
= key
.getDocument();
444 for (final FileElement fileElement
: ((SingleRootFileViewProvider
)key
).getKnownTreeRoots()) {
445 fileElement
.acceptTree(
446 new RecursiveTreeElementWalkingVisitor(){
447 protected boolean visitNode(TreeElement element
) {
448 if(CodeEditUtil
.isMarkedToReformatBefore(element
)) {
449 CodeEditUtil
.markToReformatBefore(element
, false);
450 rangesToProcess
.put(document
.createRangeMarker(element
.getStartOffset(), element
.getStartOffset()),
451 new ReformatWithHeadingWhitespaceAction());
459 private static void adjustIndentationInRange(final PsiFile file
, final Document document
, final TextRange
[] indents
, final int indentAdjustment
) {
460 final Helper formatHelper
= HelperFactory
.createHelper(file
.getFileType(), file
.getProject());
461 final CharSequence charsSequence
= document
.getCharsSequence();
462 for (final TextRange indent
: indents
) {
463 final String oldIndentStr
= charsSequence
.subSequence(indent
.getStartOffset() + 1, indent
.getEndOffset()).toString();
464 final int oldIndent
= formatHelper
.getIndent(oldIndentStr
, true);
465 final String newIndentStr
= formatHelper
.fillIndent(Math
.max(oldIndent
+ indentAdjustment
, 0));
466 document
.replaceString(indent
.getStartOffset() + 1, indent
.getEndOffset(), newIndentStr
);
470 private static int getNewIndent(final PsiFile psiFile
, final int firstWhitespace
) {
471 final Helper formatHelper
= HelperFactory
.createHelper(psiFile
.getFileType(), psiFile
.getProject());
472 final Document document
= psiFile
.getViewProvider().getDocument();
473 final int startOffset
= document
.getLineStartOffset(document
.getLineNumber(firstWhitespace
));
474 int endOffset
= startOffset
;
475 final CharSequence charsSequence
= document
.getCharsSequence();
476 while(Character
.isWhitespace(charsSequence
.charAt(endOffset
++)));
477 final String newIndentStr
= charsSequence
.subSequence(startOffset
, endOffset
- 1).toString();
478 return formatHelper
.getIndent(newIndentStr
, true);
481 public boolean isDisabled() {
482 return myDisabledCounter
> 0;
485 private CodeFormatterFacade
getFormatterFacade(final FileViewProvider viewProvider
) {
486 final CodeStyleSettings styleSettings
= CodeStyleSettingsManager
.getSettings(myPsiManager
.getProject());
487 final PsiDocumentManager documentManager
= PsiDocumentManager
.getInstance(myPsiManager
.getProject());
488 final Document document
= viewProvider
.getDocument();
489 final FileType fileType
= viewProvider
.getVirtualFile().getFileType();
490 final Helper helper
= HelperFactory
.createHelper(fileType
, myPsiManager
.getProject());
491 final CodeFormatterFacade codeFormatter
= new CodeFormatterFacade(styleSettings
, helper
);
493 documentManager
.commitDocument(document
);
494 return codeFormatter
;
497 private interface PostponedAction
{
498 void processRange(RangeMarker marker
, final FileViewProvider viewProvider
);
501 private class ReformatAction
implements PostponedAction
{
502 public void processRange(RangeMarker marker
, final FileViewProvider viewProvider
) {
503 final CodeFormatterFacade codeFormatter
= getFormatterFacade(viewProvider
);
504 codeFormatter
.processTextWithoutHeadWhitespace(viewProvider
.getPsi(viewProvider
.getBaseLanguage()),
505 marker
.getStartOffset(), marker
.getEndOffset());
509 private class ReformatWithHeadingWhitespaceAction
extends ReformatAction
{
510 public void processRange(RangeMarker marker
, final FileViewProvider viewProvider
) {
511 final CodeFormatterFacade codeFormatter
= getFormatterFacade(viewProvider
);
512 codeFormatter
.processText(viewProvider
.getPsi(viewProvider
.getBaseLanguage()), marker
.getStartOffset(),
513 marker
.getStartOffset() == marker
.getEndOffset() ? marker
.getEndOffset() + 1: marker
.getEndOffset());
519 private static class ReindentAction
implements PostponedAction
{
520 private final int myOldIndent
;
522 public ReindentAction(final int oldIndent
) {
523 myOldIndent
= oldIndent
;
526 public void processRange(RangeMarker marker
, final FileViewProvider viewProvider
) {
527 final Document document
= viewProvider
.getDocument();
528 final PsiFile psiFile
= viewProvider
.getPsi(viewProvider
.getBaseLanguage());
529 final CharSequence charsSequence
= document
.getCharsSequence().subSequence(marker
.getStartOffset(),
530 marker
.getEndOffset());
531 final int oldIndent
= getOldIndent();
532 final TextRange
[] whitespaces
= CharArrayUtil
.getIndents(charsSequence
, marker
.getStartOffset());
533 final int indentAdjustment
= getNewIndent(psiFile
, marker
.getStartOffset()) - oldIndent
;
534 if(indentAdjustment
!= 0)
535 adjustIndentationInRange(psiFile
, document
, whitespaces
, indentAdjustment
);
538 private int getOldIndent() {
544 public void projectOpened() {
547 public void projectClosed() {
551 public String
getComponentName() {
552 return "Postponed reformatting model";
555 public void initComponent() {
558 public void disposeComponent() {