diff --git a/java/testFramework/src/com/intellij/codeInsight/daemon/LightDaemonAnalyzerTestCase.java b/java/testFramework/src/com/intellij/codeInsight/daemon/LightDaemonAnalyzerTestCase.java index 2cc216d4a460..206e65d420a2 100644 --- a/java/testFramework/src/com/intellij/codeInsight/daemon/LightDaemonAnalyzerTestCase.java +++ b/java/testFramework/src/com/intellij/codeInsight/daemon/LightDaemonAnalyzerTestCase.java @@ -32,6 +32,7 @@ import com.intellij.testFramework.HighlightTestInfo; import com.intellij.testFramework.LightCodeInsightTestCase; import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl; import com.intellij.util.ArrayUtil; +import gnu.trove.TIntArrayList; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -139,7 +140,14 @@ public abstract class LightDaemonAnalyzerTestCase extends LightCodeInsightTestCa protected List doHighlighting() { PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); - int[] toIgnore = doFolding() ? ArrayUtil.EMPTY_INT_ARRAY : new int[]{Pass.UPDATE_FOLDING}; + TIntArrayList toIgnoreList = new TIntArrayList(); + if (!doFolding()) { + toIgnoreList.add(Pass.UPDATE_FOLDING); + } + if (!doInspections()) { + toIgnoreList.add(Pass.LOCAL_INSPECTIONS); + } + int[] toIgnore = toIgnoreList.isEmpty() ? ArrayUtil.EMPTY_INT_ARRAY : toIgnoreList.toNativeArray(); Editor editor = getEditor(); PsiFile file = getFile(); if (editor instanceof EditorWindow) { @@ -157,4 +165,8 @@ public abstract class LightDaemonAnalyzerTestCase extends LightCodeInsightTestCa protected boolean doFolding() { return false; } + + protected boolean doInspections() { + return true; + } } diff --git a/platform/lang-impl/src/com/intellij/psi/formatter/common/AbstractBlock.java b/platform/lang-impl/src/com/intellij/psi/formatter/common/AbstractBlock.java index 6ee8507283cd..3897c09db4a4 100644 --- a/platform/lang-impl/src/com/intellij/psi/formatter/common/AbstractBlock.java +++ b/platform/lang-impl/src/com/intellij/psi/formatter/common/AbstractBlock.java @@ -17,18 +17,18 @@ package com.intellij.psi.formatter.common; import com.intellij.formatting.*; +import com.intellij.injected.editor.DocumentWindow; import com.intellij.lang.ASTNode; -import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiLanguageInjectionHost; import com.intellij.psi.formatter.FormatterUtil; import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; +import com.intellij.util.containers.ContainerUtilRt; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -79,26 +79,25 @@ public abstract class AbstractBlock implements ASTBlock { if (file == null) { return EMPTY; } - - if (InjectedLanguageUtil.areInjectionsProcessed(file) && InjectedLanguageUtil.getCachedInjectedDocuments(file).isEmpty()) { + + if (InjectedLanguageUtil.getCachedInjectedDocuments(file).isEmpty()) { return EMPTY; } - final Ref injectedRef = new Ref(); - InjectedLanguageUtil.enumerate(psi, file, true, new PsiLanguageInjectionHost.InjectedPsiVisitor() { - @Override - public void visit(@NotNull PsiFile injectedPsi, @NotNull List places) { - if (injectedRef.get() == null) { - injectedRef.set(injectedPsi); + TextRange blockRange = myNode.getTextRange(); + List documentWindows = InjectedLanguageUtil.getCachedInjectedDocuments(file); + for (DocumentWindow documentWindow : documentWindows) { + int startOffset = documentWindow.injectedToHost(0); + int endOffset = startOffset + documentWindow.getTextLength(); + if (blockRange.containsRange(startOffset, endOffset)) { + PsiFile injected = PsiDocumentManager.getInstance(psi.getProject()).getCachedPsiFile(documentWindow); + if (injected != null) { + List result = ContainerUtilRt.newArrayList(); + DefaultInjectedLanguageBlockBuilder builder = new DefaultInjectedLanguageBlockBuilder(((SettingsAwareBlock)this).getSettings()); + builder.addInjectedBlocks(result, myNode, getWrap(), getAlignment(), getIndent()); + return result; } } - }); - PsiFile injected = injectedRef.get(); - if (injected != null && myNode.getTextLength() >= injected.getTextLength()) { - List result = new ArrayList(); - DefaultInjectedLanguageBlockBuilder builder = new DefaultInjectedLanguageBlockBuilder(((SettingsAwareBlock)this).getSettings()); - builder.addInjectedBlocks(result, myNode, getWrap(), getAlignment(), getIndent()); - return result; } return EMPTY; } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeFormatterFacade.java b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeFormatterFacade.java index 68ffa4588e9d..c6804774ba01 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeFormatterFacade.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeFormatterFacade.java @@ -34,14 +34,12 @@ import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.Segment; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.UserDataHolder; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiLanguageInjectionHost; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.formatter.DocumentBasedFormattingModel; @@ -58,8 +56,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.awt.*; -import java.util.Collections; -import java.util.LinkedHashSet; import java.util.List; public class CodeFormatterFacade { @@ -236,59 +232,37 @@ public class CodeFormatterFacade { PsiFile file = psi.getContainingFile(); - // We use a set here because we encountered a situation when more than one PSI leaf points to the same injected fragment - // (at least for sql injected into sql). - final LinkedHashSet injectedFileRangesSet = ContainerUtilRt.newLinkedHashSet(); - if (InjectedLanguageUtil.areInjectionsProcessed(file)) { - for (DocumentWindow window : InjectedLanguageUtil.getCachedInjectedDocuments(file)) { - injectedFileRangesSet.add(TextRange.create(window.injectedToHost(0), window.injectedToHost(window.getTextLength()))); - } - } - else if (!file.getProject().isDefault()) { - PsiLanguageInjectionHost.InjectedPsiVisitor visitor = new PsiLanguageInjectionHost.InjectedPsiVisitor() { - @Override - public void visit(@NotNull PsiFile injectedPsi, @NotNull List places) { - for (PsiLanguageInjectionHost.Shred place : places) { - Segment rangeMarker = place.getHostRangeMarker(); - injectedFileRangesSet.add(TextRange.create(rangeMarker.getStartOffset(), rangeMarker.getEndOffset())); - } + PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(file.getProject()); + List injectedDocuments = InjectedLanguageUtil.getCachedInjectedDocuments(file); + if (!injectedDocuments.isEmpty()) { + List injectedDocumentsToUse = ContainerUtilRt.newArrayList(injectedDocuments); + for (DocumentWindow window : injectedDocumentsToUse) { + if (!window.isValid()) { + // There is a possible case that format pre-processor modifies target document in a way that some injections are dropped. + continue; } - }; + PsiFile injected = psiDocumentManager.getCachedPsiFile(window); + if (injected != null) { + int startHostOffset = window.injectedToHost(0); + int endHostOffset = window.injectedToHost(window.getTextLength()); + int startInjectedOffset = range.getStartOffset() > startHostOffset ? startHostOffset - range.getStartOffset() : 0; + int endInjectedOffset = injected.getTextLength(); + if (range.getEndOffset() < endHostOffset) { + endInjectedOffset -= endHostOffset - range.getEndOffset(); + } + final TextRange initialInjectedRange = TextRange.create(startInjectedOffset, endInjectedOffset); + TextRange injectedRange = initialInjectedRange; + for (PreFormatProcessor processor : Extensions.getExtensions(PreFormatProcessor.EP_NAME)) { + injectedRange = processor.process(injected.getNode(), injectedRange); + } - for (PsiElement e = PsiTreeUtil.getDeepestFirst(file); e != null; e = PsiTreeUtil.nextLeaf(e, true)) { - InjectedLanguageUtil.enumerate(e, visitor); - } - } - - if (!injectedFileRangesSet.isEmpty()) { - List ranges = ContainerUtilRt.newArrayList(injectedFileRangesSet); - Collections.reverse(ranges); - for (TextRange injectedFileRange : ranges) { - int startHostOffset = injectedFileRange.getStartOffset(); - int endHostOffset = injectedFileRange.getEndOffset(); - if (startHostOffset >= range.getStartOffset() && endHostOffset <= range.getEndOffset()) { - PsiFile injected = InjectedLanguageUtil.findInjectedPsiNoCommit(file, startHostOffset); - if (injected != null) { - int startInjectedOffset = range.getStartOffset() > startHostOffset ? startHostOffset - range.getStartOffset() : 0; - int endInjectedOffset = injected.getTextLength(); - if (range.getEndOffset() < endHostOffset) { - endInjectedOffset -= endHostOffset - range.getEndOffset(); - } - final TextRange initialInjectedRange = TextRange.create(startInjectedOffset, endInjectedOffset); - TextRange injectedRange = initialInjectedRange; - for (PreFormatProcessor processor : Extensions.getExtensions(PreFormatProcessor.EP_NAME)) { - injectedRange = processor.process(injected.getNode(), injectedRange); - } - - // Allow only range expansion (not reduction) for injected context. - if ((initialInjectedRange.getStartOffset() > injectedRange.getStartOffset() && initialInjectedRange.getStartOffset() > 0) - || (initialInjectedRange.getEndOffset() < injectedRange.getEndOffset() - && initialInjectedRange.getEndOffset() < injected.getTextLength())) - { - range = TextRange.create( - range.getStartOffset() + injectedRange.getStartOffset() - initialInjectedRange.getStartOffset(), - range.getEndOffset() + initialInjectedRange.getEndOffset() - injectedRange.getEndOffset()); - } + // Allow only range expansion (not reduction) for injected context. + if ((initialInjectedRange.getStartOffset() > injectedRange.getStartOffset() && initialInjectedRange.getStartOffset() > 0) + || (initialInjectedRange.getEndOffset() < injectedRange.getEndOffset() + && initialInjectedRange.getEndOffset() < injected.getTextLength())) { + range = TextRange.create( + range.getStartOffset() + injectedRange.getStartOffset() - initialInjectedRange.getStartOffset(), + range.getEndOffset() + initialInjectedRange.getEndOffset() - injectedRange.getEndOffset()); } } } @@ -296,6 +270,7 @@ public class CodeFormatterFacade { for(PreFormatProcessor processor: Extensions.getExtensions(PreFormatProcessor.EP_NAME)) { result = processor.process(node, result); } + return result; } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageUtil.java b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageUtil.java index 489ee10e9255..847cbdd197de 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageUtil.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageUtil.java @@ -161,6 +161,7 @@ public class InjectedLanguageUtil { return EditorWindow.create(documentWindow, (EditorImpl)hostEditor, injectedFile); } + @Nullable public static PsiFile findInjectedPsiNoCommit(@NotNull PsiFile host, int offset) { PsiElement injected = findInjectedElementNoCommit(host, offset); return injected == null ? null : injected.getContainingFile(); @@ -312,10 +313,6 @@ public class InjectedLanguageUtil { private static final Key> INJECTED_DOCS_KEY = Key.create("INJECTED_DOCS_KEY"); - public static boolean areInjectionsProcessed(@NotNull PsiFile hostPsiFile) { - return hostPsiFile.getUserData(INJECTED_DOCS_KEY) != null; - } - @NotNull public static List getCachedInjectedDocuments(@NotNull PsiFile hostPsiFile) { // modification of cachedInjectedDocuments must be under PsiLock only diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedPsiCachedValueProvider.java b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedPsiCachedValueProvider.java index ce079e1b7590..4bb7cea5424c 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedPsiCachedValueProvider.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedPsiCachedValueProvider.java @@ -27,6 +27,7 @@ import com.intellij.psi.PsiManager; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.ParameterizedCachedValueProvider; import com.intellij.psi.util.PsiModificationTracker; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; @@ -53,8 +54,8 @@ class InjectedPsiCachedValueProvider implements ParameterizedCachedValueProvider } @Nullable - static MultiHostRegistrarImpl doCompute(final PsiElement element, - InjectedLanguageManagerImpl injectedManager, + static MultiHostRegistrarImpl doCompute(@NotNull final PsiElement element, + @NotNull InjectedLanguageManagerImpl injectedManager, Project project, PsiFile hostPsiFile) { MyInjProcessor processor = new MyInjProcessor(project, hostPsiFile); diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/LeafPatcher.java b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/LeafPatcher.java index c2be42377cdb..c1fc0a8c55cf 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/LeafPatcher.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/LeafPatcher.java @@ -51,7 +51,7 @@ class LeafPatcher extends RecursiveTreeElementWalkingVisitor { } @Override - public void visitLeaf(LeafElement leaf) { + public void visitLeaf(LeafElement leaf) { String leafText = leaf instanceof ForeignLeafPsiElement ? "" : leaf.getText(); catLeafs.append(leafText); final TextRange leafRange = leaf.getTextRange();