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.
17 package com
.intellij
.psi
.impl
.source
;
19 import com
.intellij
.lang
.ASTNode
;
20 import com
.intellij
.openapi
.Disposable
;
21 import com
.intellij
.openapi
.application
.ApplicationAdapter
;
22 import com
.intellij
.openapi
.application
.ApplicationListener
;
23 import com
.intellij
.openapi
.application
.ApplicationManager
;
24 import com
.intellij
.openapi
.command
.CommandProcessor
;
25 import com
.intellij
.openapi
.diagnostic
.Logger
;
26 import com
.intellij
.openapi
.editor
.Document
;
27 import com
.intellij
.openapi
.editor
.RangeMarker
;
28 import com
.intellij
.openapi
.fileTypes
.FileType
;
29 import com
.intellij
.openapi
.fileTypes
.FileTypeManager
;
30 import com
.intellij
.openapi
.progress
.ProgressManager
;
31 import com
.intellij
.openapi
.project
.Project
;
32 import com
.intellij
.openapi
.util
.*;
33 import com
.intellij
.openapi
.vfs
.VirtualFile
;
34 import com
.intellij
.pom
.PomManager
;
35 import com
.intellij
.pom
.PomModelAspect
;
36 import com
.intellij
.pom
.event
.PomModelEvent
;
37 import com
.intellij
.pom
.tree
.TreeAspect
;
38 import com
.intellij
.pom
.tree
.events
.ChangeInfo
;
39 import com
.intellij
.pom
.tree
.events
.TreeChange
;
40 import com
.intellij
.pom
.tree
.events
.TreeChangeEvent
;
41 import com
.intellij
.psi
.*;
42 import com
.intellij
.psi
.codeStyle
.CodeStyleSettings
;
43 import com
.intellij
.psi
.codeStyle
.CodeStyleSettingsManager
;
44 import com
.intellij
.psi
.impl
.PsiTreeDebugBuilder
;
45 import com
.intellij
.psi
.impl
.source
.codeStyle
.CodeEditUtil
;
46 import com
.intellij
.psi
.impl
.source
.codeStyle
.CodeFormatterFacade
;
47 import com
.intellij
.psi
.impl
.source
.codeStyle
.Helper
;
48 import com
.intellij
.psi
.impl
.source
.codeStyle
.HelperFactory
;
49 import com
.intellij
.psi
.impl
.source
.tree
.*;
50 import com
.intellij
.psi
.impl
.source
.tree
.injected
.InjectedLanguageUtil
;
51 import com
.intellij
.util
.LocalTimeCounter
;
52 import com
.intellij
.util
.text
.CharArrayUtil
;
53 import org
.jetbrains
.annotations
.NonNls
;
54 import org
.jetbrains
.annotations
.NotNull
;
55 import org
.jetbrains
.annotations
.TestOnly
;
59 public class PostprocessReformattingAspect
implements PomModelAspect
, Disposable
{
60 private static final Logger LOG
= Logger
.getInstance("#com.intellij.psi.impl.source.PostprocessReformatingAspect");
61 private final Project myProject
;
62 private final PsiManager myPsiManager
;
63 private final TreeAspect myTreeAspect
;
64 private final Map
<FileViewProvider
, List
<ASTNode
>> myReformatElements
= new HashMap
<FileViewProvider
, List
<ASTNode
>>();
65 private volatile int myDisabledCounter
= 0;
66 private final Set
<FileViewProvider
> myUpdatedProviders
= new HashSet
<FileViewProvider
>();
68 private final ApplicationListener myApplicationListener
= new ApplicationAdapter() {
69 public void writeActionStarted(final Object action
) {
70 final CommandProcessor processor
= CommandProcessor
.getInstance();
71 if (processor
!= null) {
72 final Project project
= processor
.getCurrentCommandProject();
73 if (project
== myProject
) {
79 public void writeActionFinished(final Object action
) {
80 final CommandProcessor processor
= CommandProcessor
.getInstance();
81 if (processor
!= null) {
82 final Project project
= processor
.getCurrentCommandProject();
83 if (project
== myProject
) {
84 decrementPostponedCounter();
90 public PostprocessReformattingAspect(Project project
, PsiManager psiManager
, TreeAspect treeAspect
) {
92 myPsiManager
= psiManager
;
93 myTreeAspect
= treeAspect
;
94 PomManager
.getModel(psiManager
.getProject())
95 .registerAspect(PostprocessReformattingAspect
.class, this, Collections
.singleton((PomModelAspect
)treeAspect
));
97 ApplicationManager
.getApplication().addApplicationListener(myApplicationListener
);
98 Disposer
.register(project
, this);
101 public void dispose() {
102 ApplicationManager
.getApplication().removeApplicationListener(myApplicationListener
);
105 public void disablePostprocessFormattingInside(final Runnable runnable
) {
106 disablePostprocessFormattingInside(new NullableComputable
<Object
>() {
107 public Object
compute() {
114 public <T
> T
disablePostprocessFormattingInside(Computable
<T
> computable
) {
117 return computable
.compute();
121 LOG
.assertTrue(myDisabledCounter
> 0 || !isDisabled());
125 private int myPostponedCounter
= 0;
127 public void postponeFormattingInside(final Runnable runnable
) {
128 postponeFormattingInside(new NullableComputable
<Object
>() {
129 public Object
compute() {
136 public <T
> T
postponeFormattingInside(Computable
<T
> computable
) {
138 //if(myPostponedCounter == 0) myDisabled = false;
139 myPostponedCounter
++;
140 return computable
.compute();
143 decrementPostponedCounter();
147 private void decrementPostponedCounter() {
148 if (--myPostponedCounter
== 0) {
149 if (!ApplicationManager
.getApplication().isWriteAccessAllowed()) {
150 ApplicationManager
.getApplication().runWriteAction(new Runnable() {
152 doPostponedFormatting();
157 doPostponedFormatting();
163 private final Object LOCK
= new Object();
165 private void atomic(Runnable r
) {
166 synchronized (LOCK
) {
167 ProgressManager
.getInstance().executeNonCancelableSection(r
);
171 public void update(final PomModelEvent event
) {
172 atomic(new Runnable() {
174 if (isDisabled() || myPostponedCounter
== 0 && !ApplicationManager
.getApplication().isUnitTestMode()) return;
175 final TreeChangeEvent changeSet
= (TreeChangeEvent
)event
.getChangeSet(myTreeAspect
);
176 if (changeSet
== null) return;
177 final PsiElement psiElement
= changeSet
.getRootElement().getPsi();
178 if (psiElement
== null) return;
179 PsiFile containingFile
= InjectedLanguageUtil
.getTopLevelFile(psiElement
);
180 final FileViewProvider viewProvider
= containingFile
.getViewProvider();
182 if (!viewProvider
.isEventSystemEnabled()) return;
183 myUpdatedProviders
.add(viewProvider
);
184 for (final ASTNode node
: changeSet
.getChangedElements()) {
185 final TreeChange treeChange
= changeSet
.getChangesByElement(node
);
186 for (final ASTNode affectedChild
: treeChange
.getAffectedChildren()) {
187 final ChangeInfo childChange
= treeChange
.getChangeByChild(affectedChild
);
188 switch (childChange
.getChangeType()) {
190 case ChangeInfo
.REPLACE
:
191 postponeFormatting(viewProvider
, affectedChild
);
193 case ChangeInfo
.CONTENTS_CHANGED
:
194 if (!CodeEditUtil
.isNodeGenerated(affectedChild
)) {
195 ((TreeElement
)affectedChild
).acceptTree(new RecursiveTreeElementWalkingVisitor() {
196 protected void visitNode(TreeElement element
) {
197 if (CodeEditUtil
.isNodeGenerated(element
)) {
198 postponeFormatting(viewProvider
, element
);
201 super.visitNode(element
);
213 public void doPostponedFormatting() {
214 atomic(new Runnable() {
216 if (isDisabled()) return;
218 FileViewProvider
[] viewProviders
= myUpdatedProviders
.toArray(new FileViewProvider
[myUpdatedProviders
.size()]);
219 for (final FileViewProvider viewProvider
: viewProviders
) {
220 doPostponedFormatting(viewProvider
);
224 LOG
.assertTrue(myReformatElements
.isEmpty());
230 public void doPostponedFormatting(final FileViewProvider viewProvider
) {
231 atomic(new Runnable() {
233 if (isDisabled() || !myUpdatedProviders
.contains(viewProvider
)) return;
236 disablePostprocessFormattingInside(new Runnable() {
238 doPostponedFormattingInner(viewProvider
);
243 myUpdatedProviders
.remove(viewProvider
);
244 myReformatElements
.remove(viewProvider
);
250 public boolean isViewProviderLocked(final FileViewProvider fileViewProvider
) {
251 return myReformatElements
.containsKey(fileViewProvider
);
254 public static PostprocessReformattingAspect
getInstance(Project project
) {
255 return project
.getComponent(PostprocessReformattingAspect
.class);
258 private void postponeFormatting(final FileViewProvider viewProvider
, final ASTNode child
) {
259 if (!CodeEditUtil
.isNodeGenerated(child
) && child
.getElementType() != TokenType
.WHITE_SPACE
) {
260 final int oldIndent
= CodeEditUtil
.getOldIndentation(child
);
261 LOG
.assertTrue(oldIndent
>= 0,
262 "for not generated items old indentation must be defined: element=" + child
+ ", text=" + child
.getText());
264 List
<ASTNode
> list
= myReformatElements
.get(viewProvider
);
266 list
= new ArrayList
<ASTNode
>();
267 myReformatElements
.put(viewProvider
, list
);
272 private void doPostponedFormattingInner(final FileViewProvider key
) {
275 final List
<ASTNode
> astNodes
= myReformatElements
.remove(key
);
276 final Document document
= key
.getDocument();
277 // Sort ranges by end offsets so that we won't need any offset adjustment after reformat or reindent
278 if (document
== null /*|| documentManager.isUncommited(document) TODO */) return;
280 final VirtualFile virtualFile
= key
.getVirtualFile();
281 if (!virtualFile
.isValid()) return;
283 final TreeMap
<RangeMarker
, PostponedAction
> rangesToProcess
= new TreeMap
<RangeMarker
, PostponedAction
>(new Comparator
<RangeMarker
>() {
284 public int compare(final RangeMarker o1
, final RangeMarker o2
) {
285 if (o1
.equals(o2
)) return 0;
286 final int diff
= o2
.getEndOffset() - o1
.getEndOffset();
288 if (o1
.getStartOffset() == o2
.getStartOffset()) return 0;
289 if (o1
.getStartOffset() == o1
.getEndOffset()) return -1; // empty ranges first
290 if (o2
.getStartOffset() == o2
.getEndOffset()) return 1; // empty ranges first
291 return o1
.getStartOffset() - o2
.getStartOffset();
297 // process all roots in viewProvider to find marked for reformat before elements and create appropriate ragge markers
298 handleReformatMarkers(key
, rangesToProcess
);
300 // then we create ranges by changed nodes. One per node. There ranges can instersect. Ranges are sorted by end offset.
301 if (astNodes
!= null) createActionsMap(astNodes
, key
, rangesToProcess
);
303 if ("true".equals(System
.getProperty("check.psi.is.valid")) && ApplicationManager
.getApplication().isUnitTestMode()) {
304 checkPsiIsCorrect(key
);
307 while (!rangesToProcess
.isEmpty()) {
308 // now we have to normalize actions so that they not intersect and ordered in most appropriate way
309 // (free reformating -> reindent -> formating under reindent)
310 final List
<Pair
<RangeMarker
, ?
extends PostponedAction
>> normalizedActions
=
311 normalizeAndReorderPostponedActions(rangesToProcess
, document
);
313 // only in following loop real changes in document are made
314 for (final Pair
<RangeMarker
, ?
extends PostponedAction
> normalizedAction
: normalizedActions
) {
315 CodeStyleSettings settings
= CodeStyleSettingsManager
.getSettings(myPsiManager
.getProject());
316 boolean old
= settings
.ENABLE_JAVADOC_FORMATTING
;
317 settings
.ENABLE_JAVADOC_FORMATTING
= false;
319 normalizedAction
.getSecond().processRange(normalizedAction
.getFirst(), key
);
322 settings
.ENABLE_JAVADOC_FORMATTING
= old
;
328 private void checkPsiIsCorrect(final FileViewProvider key
) {
329 PsiFile actualPsi
= key
.getPsi(key
.getBaseLanguage());
331 PsiTreeDebugBuilder treeDebugBuilder
= new PsiTreeDebugBuilder().setShowErrorElements(false).setShowWhiteSpaces(false);
333 String actualPsiTree
= treeDebugBuilder
.psiToString(actualPsi
);
335 String fileName
= key
.getVirtualFile().getName();
336 PsiFile psi
= PsiFileFactory
.getInstance(myProject
)
337 .createFileFromText(fileName
, FileTypeManager
.getInstance().getFileTypeByFileName(fileName
), actualPsi
.getNode().getText(),
338 LocalTimeCounter
.currentTime(), false);
340 if (actualPsi
.getClass().equals(psi
.getClass())) {
341 String expectedPsi
= treeDebugBuilder
.psiToString(psi
);
343 if (!expectedPsi
.equals(actualPsiTree
)) {
344 myReformatElements
.clear();
345 assert expectedPsi
.equals(actualPsiTree
) : "Refactored psi should be the same as result of parsing";
352 private List
<Pair
<RangeMarker
, ?
extends PostponedAction
>> normalizeAndReorderPostponedActions(final TreeMap
<RangeMarker
, PostponedAction
> rangesToProcess
,
354 final List
<Pair
<RangeMarker
, ReformatAction
>> freeFormatingActions
= new ArrayList
<Pair
<RangeMarker
, ReformatAction
>>();
355 final List
<Pair
<RangeMarker
, ReindentAction
>> indentActions
= new ArrayList
<Pair
<RangeMarker
, ReindentAction
>>();
357 RangeMarker accumulatedRange
= null;
358 PostponedAction accumulatedRangeAction
= null;
359 Iterator
<Map
.Entry
<RangeMarker
, PostponedAction
>> iterator
= rangesToProcess
.entrySet().iterator();
360 while (iterator
.hasNext()) {
361 final Map
.Entry
<RangeMarker
, PostponedAction
> entry
= iterator
.next();
362 final RangeMarker textRange
= entry
.getKey();
363 final PostponedAction action
= entry
.getValue();
364 if (accumulatedRange
== null) {
365 accumulatedRange
= textRange
;
366 accumulatedRangeAction
= action
;
369 else if (accumulatedRange
.getStartOffset() > textRange
.getEndOffset() ||
370 (accumulatedRange
.getStartOffset() == textRange
.getEndOffset() &&
371 !canStickActionsTogether(accumulatedRangeAction
, accumulatedRange
, action
, textRange
))) {
372 // action can be pushed
373 if (accumulatedRangeAction
instanceof ReindentAction
) {
374 indentActions
.add(new Pair
<RangeMarker
, ReindentAction
>(accumulatedRange
, (ReindentAction
)accumulatedRangeAction
));
377 freeFormatingActions
.add(new Pair
<RangeMarker
, ReformatAction
>(accumulatedRange
, (ReformatAction
)accumulatedRangeAction
));
380 accumulatedRange
= textRange
;
381 accumulatedRangeAction
= action
;
384 else if (accumulatedRangeAction
instanceof ReformatAction
&& action
instanceof ReindentAction
) {
385 // split accumulated reformat range into two
386 if (accumulatedRange
.getStartOffset() < textRange
.getStartOffset()) {
387 final RangeMarker endOfRange
= document
.createRangeMarker(accumulatedRange
.getStartOffset(), textRange
.getStartOffset());
388 // add heading reformat part
389 rangesToProcess
.put(endOfRange
, accumulatedRangeAction
);
390 // and manage heading whitespace because formatter does not edit it in previous action
391 iterator
= rangesToProcess
.entrySet().iterator();
392 //noinspection StatementWithEmptyBody
393 while (iterator
.next().getKey() != textRange
) ;
395 final RangeMarker rangeToProcess
= document
.createRangeMarker(textRange
.getEndOffset(), accumulatedRange
.getEndOffset());
396 freeFormatingActions
.add(new Pair
<RangeMarker
, ReformatAction
>(rangeToProcess
, new ReformatWithHeadingWhitespaceAction()));
397 accumulatedRange
= textRange
;
398 accumulatedRangeAction
= action
;
402 if (!(accumulatedRangeAction
instanceof ReindentAction
)) {
404 if (accumulatedRangeAction
instanceof ReformatAction
&&
405 action
instanceof ReformatWithHeadingWhitespaceAction
&&
406 accumulatedRange
.getStartOffset() == textRange
.getStartOffset() ||
407 accumulatedRangeAction
instanceof ReformatWithHeadingWhitespaceAction
&&
408 action
instanceof ReformatAction
&&
409 accumulatedRange
.getStartOffset() < textRange
.getStartOffset()) {
410 accumulatedRangeAction
= action
;
412 accumulatedRange
= document
.createRangeMarker(Math
.min(accumulatedRange
.getStartOffset(), textRange
.getStartOffset()),
413 Math
.max(accumulatedRange
.getEndOffset(), textRange
.getEndOffset()));
415 else if (action
instanceof ReindentAction
) {
417 } // TODO[ik]: need to be fixed to correctly process indent inside indent
420 if (accumulatedRange
!= null) {
421 if (accumulatedRangeAction
instanceof ReindentAction
) {
422 indentActions
.add(new Pair
<RangeMarker
, ReindentAction
>(accumulatedRange
, (ReindentAction
)accumulatedRangeAction
));
425 freeFormatingActions
.add(new Pair
<RangeMarker
, ReformatAction
>(accumulatedRange
, (ReformatAction
)accumulatedRangeAction
));
429 final List
<Pair
<RangeMarker
, ?
extends PostponedAction
>> result
=
430 new ArrayList
<Pair
<RangeMarker
, ?
extends PostponedAction
>>(rangesToProcess
.size());
431 Collections
.reverse(freeFormatingActions
);
432 Collections
.reverse(indentActions
);
433 result
.addAll(freeFormatingActions
);
434 result
.addAll(indentActions
);
438 private boolean canStickActionsTogether(final PostponedAction currentAction
,
439 final RangeMarker currentRange
,
440 final PostponedAction nextAction
,
441 final RangeMarker nextRange
) {
442 // empty reformat markers can't sticked together with any action
443 if (nextAction
instanceof ReformatWithHeadingWhitespaceAction
&& nextRange
.getStartOffset() == nextRange
.getEndOffset()) return false;
444 if (currentAction
instanceof ReformatWithHeadingWhitespaceAction
&& currentRange
.getStartOffset() == currentRange
.getEndOffset()) {
447 // reindent actions can't be sticked at all
448 return !(currentAction
instanceof ReindentAction
);
451 private void createActionsMap(final List
<ASTNode
> astNodes
,
452 final FileViewProvider provider
,
453 final TreeMap
<RangeMarker
, PostponedAction
> rangesToProcess
) {
454 final Set
<ASTNode
> nodesToProcess
= new HashSet
<ASTNode
>(astNodes
);
455 final Document document
= provider
.getDocument();
456 for (final ASTNode node
: astNodes
) {
457 nodesToProcess
.remove(node
);
458 final FileElement fileElement
= TreeUtil
.getFileElement((TreeElement
)node
);
459 if (fileElement
== null || ((PsiFile
)fileElement
.getPsi()).getViewProvider() != provider
) continue;
460 final boolean isGenerated
= CodeEditUtil
.isNodeGenerated(node
);
462 ((TreeElement
)node
).acceptTree(new RecursiveTreeElementVisitor() {
463 boolean inGeneratedContext
= !isGenerated
;
465 protected boolean visitNode(TreeElement element
) {
466 if (nodesToProcess
.contains(element
)) return false;
467 final boolean currentNodeGenerated
= CodeEditUtil
.isNodeGenerated(element
);
468 CodeEditUtil
.setNodeGenerated(element
, false);
469 if (currentNodeGenerated
&& !inGeneratedContext
) {
470 rangesToProcess
.put(document
.createRangeMarker(element
.getTextRange()), new ReformatAction());
471 inGeneratedContext
= true;
473 if (!currentNodeGenerated
&& inGeneratedContext
) {
474 if (element
.getElementType() == TokenType
.WHITE_SPACE
) return false;
475 final int oldIndent
= CodeEditUtil
.getOldIndentation(element
);
476 LOG
.assertTrue(oldIndent
>= 0, "for not generated items old indentation must be defined");
477 rangesToProcess
.put(document
.createRangeMarker(element
.getTextRange()), new ReindentAction(oldIndent
));
478 inGeneratedContext
= false;
484 public void visitComposite(CompositeElement composite
) {
485 boolean oldGeneratedContext
= inGeneratedContext
;
486 super.visitComposite(composite
);
487 inGeneratedContext
= oldGeneratedContext
;
491 public void visitLeaf(LeafElement leaf
) {
492 boolean oldGeneratedContext
= inGeneratedContext
;
493 super.visitLeaf(leaf
);
494 inGeneratedContext
= oldGeneratedContext
;
500 private void handleReformatMarkers(final FileViewProvider key
, final TreeMap
<RangeMarker
, PostponedAction
> rangesToProcess
) {
501 final Document document
= key
.getDocument();
502 for (final FileElement fileElement
: ((SingleRootFileViewProvider
)key
).getKnownTreeRoots()) {
503 fileElement
.acceptTree(new RecursiveTreeElementWalkingVisitor() {
504 protected void visitNode(TreeElement element
) {
505 if (CodeEditUtil
.isMarkedToReformatBefore(element
)) {
506 CodeEditUtil
.markToReformatBefore(element
, false);
507 rangesToProcess
.put(document
.createRangeMarker(element
.getStartOffset(), element
.getStartOffset()),
508 new ReformatWithHeadingWhitespaceAction());
510 super.visitNode(element
);
516 private static void adjustIndentationInRange(final PsiFile file
,
517 final Document document
,
518 final TextRange
[] indents
,
519 final int indentAdjustment
) {
520 final Helper formatHelper
= HelperFactory
.createHelper(file
.getFileType(), file
.getProject());
521 final CharSequence charsSequence
= document
.getCharsSequence();
522 for (final TextRange indent
: indents
) {
523 final String oldIndentStr
= charsSequence
.subSequence(indent
.getStartOffset() + 1, indent
.getEndOffset()).toString();
524 final int oldIndent
= formatHelper
.getIndent(oldIndentStr
, true);
525 final String newIndentStr
= formatHelper
.fillIndent(Math
.max(oldIndent
+ indentAdjustment
, 0));
526 document
.replaceString(indent
.getStartOffset() + 1, indent
.getEndOffset(), newIndentStr
);
530 private static int getNewIndent(final PsiFile psiFile
, final int firstWhitespace
) {
531 final Helper formatHelper
= HelperFactory
.createHelper(psiFile
.getFileType(), psiFile
.getProject());
532 final Document document
= psiFile
.getViewProvider().getDocument();
533 final int startOffset
= document
.getLineStartOffset(document
.getLineNumber(firstWhitespace
));
534 int endOffset
= startOffset
;
535 final CharSequence charsSequence
= document
.getCharsSequence();
536 while (Character
.isWhitespace(charsSequence
.charAt(endOffset
++))) ;
537 final String newIndentStr
= charsSequence
.subSequence(startOffset
, endOffset
- 1).toString();
538 return formatHelper
.getIndent(newIndentStr
, true);
541 public boolean isDisabled() {
542 return myDisabledCounter
> 0;
545 private CodeFormatterFacade
getFormatterFacade(final FileViewProvider viewProvider
) {
546 final CodeStyleSettings styleSettings
= CodeStyleSettingsManager
.getSettings(myPsiManager
.getProject());
547 final PsiDocumentManager documentManager
= PsiDocumentManager
.getInstance(myPsiManager
.getProject());
548 final Document document
= viewProvider
.getDocument();
549 final FileType fileType
= viewProvider
.getVirtualFile().getFileType();
550 final Helper helper
= HelperFactory
.createHelper(fileType
, myPsiManager
.getProject());
551 final CodeFormatterFacade codeFormatter
= new CodeFormatterFacade(styleSettings
, helper
);
553 documentManager
.commitDocument(document
);
554 return codeFormatter
;
557 private interface PostponedAction
{
558 void processRange(RangeMarker marker
, final FileViewProvider viewProvider
);
561 private class ReformatAction
implements PostponedAction
{
562 public void processRange(RangeMarker marker
, final FileViewProvider viewProvider
) {
563 final CodeFormatterFacade codeFormatter
= getFormatterFacade(viewProvider
);
564 codeFormatter
.processTextWithoutHeadWhitespace(viewProvider
.getPsi(viewProvider
.getBaseLanguage()), marker
.getStartOffset(),
565 marker
.getEndOffset());
569 private class ReformatWithHeadingWhitespaceAction
extends ReformatAction
{
570 public void processRange(RangeMarker marker
, final FileViewProvider viewProvider
) {
571 final CodeFormatterFacade codeFormatter
= getFormatterFacade(viewProvider
);
572 codeFormatter
.processText(viewProvider
.getPsi(viewProvider
.getBaseLanguage()), marker
.getStartOffset(),
573 marker
.getStartOffset() == marker
.getEndOffset() ? marker
.getEndOffset() + 1 : marker
.getEndOffset());
579 private static class ReindentAction
implements PostponedAction
{
580 private final int myOldIndent
;
582 public ReindentAction(final int oldIndent
) {
583 myOldIndent
= oldIndent
;
586 public void processRange(RangeMarker marker
, final FileViewProvider viewProvider
) {
587 final Document document
= viewProvider
.getDocument();
588 final PsiFile psiFile
= viewProvider
.getPsi(viewProvider
.getBaseLanguage());
589 final CharSequence charsSequence
= document
.getCharsSequence().subSequence(marker
.getStartOffset(), marker
.getEndOffset());
590 final int oldIndent
= getOldIndent();
591 final TextRange
[] whitespaces
= CharArrayUtil
.getIndents(charsSequence
, marker
.getStartOffset());
592 final int indentAdjustment
= getNewIndent(psiFile
, marker
.getStartOffset()) - oldIndent
;
593 if (indentAdjustment
!= 0) adjustIndentationInRange(psiFile
, document
, whitespaces
, indentAdjustment
);
596 private int getOldIndent() {
602 public void projectOpened() {
605 public void projectClosed() {
610 public String
getComponentName() {
611 return "Postponed reformatting model";
614 public void initComponent() {
617 public void disposeComponent() {
621 public void clear() {
622 myReformatElements
.clear();