From 8f28a442d5d15d66a7027717ccb6bc718c5deee5 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Mon, 1 Aug 2011 17:25:21 +0400 Subject: [PATCH] null injection + IDEA-71554 --- .../JavaConcatenationInjectorManager.java | 170 +++++++++--------- .../source/tree/injected/MyTestInjector.java | 10 +- .../tree/java/PsiPolyadicExpressionImpl.java | 12 +- .../daemon/LightDaemonAnalyzerTestCase.java | 5 + .../injected/editor/EditorWindow.java | 22 +-- .../tree/injected/InjectedLanguageUtil.java | 61 +++---- .../tree/injected/MultiHostRegistrarImpl.java | 5 + 7 files changed, 151 insertions(+), 134 deletions(-) diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/injected/JavaConcatenationInjectorManager.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/injected/JavaConcatenationInjectorManager.java index 8c53cbb84aa6..78b1057d9843 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/injected/JavaConcatenationInjectorManager.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/injected/JavaConcatenationInjectorManager.java @@ -32,7 +32,6 @@ import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -72,61 +71,59 @@ public class JavaConcatenationInjectorManager implements ModificationTracker { private static class ConcatenationPsiCachedValueProvider implements ParameterizedCachedValueProvider { public CachedValueProvider.Result compute(PsiElement context) { - PsiElement element = context; - PsiElement parent = context.getParent(); - while (parent instanceof PsiPolyadicExpression && ((PsiPolyadicExpression)parent).getOperationTokenType() == JavaTokenType.PLUS - || parent instanceof PsiAssignmentExpression && ((PsiAssignmentExpression)parent).getOperationTokenType() == JavaTokenType.PLUSEQ - || parent instanceof PsiConditionalExpression && ((PsiConditionalExpression)parent).getCondition() != element - || parent instanceof PsiTypeCastExpression - || parent instanceof PsiParenthesizedExpression) { - element = parent; - parent = parent.getParent(); - } - - PsiElement[] operands; - PsiElement anchor; - if (element instanceof PsiPolyadicExpression || element instanceof PsiAssignmentExpression) { - List operandList = new ArrayList(); - collectOperands((PsiExpression)element, operandList); - operands = PsiUtilBase.toPsiElementArray(operandList); - anchor = element; - } - else { - operands = new PsiElement[]{context}; - anchor = context; - } Project project = context.getProject(); - - MultiHostRegistrarImpl registrar = new MultiHostRegistrarImpl(project, context.getContainingFile(), anchor); - JavaConcatenationInjectorManager concatenationInjectorManager = getInstance(project); - for (ConcatenationAwareInjector concatenationInjector : concatenationInjectorManager.myConcatenationInjectors) { - concatenationInjector.getLanguagesToInject(registrar, operands); - } - - CachedValueProvider.Result result = CachedValueProvider.Result.create(registrar, PsiModificationTracker.MODIFICATION_COUNT, concatenationInjectorManager); - - ParameterizedCachedValue cachedValue; - if (registrar.result != null) { - // store this everywhere - cachedValue = CachedValuesManager.getManager(context.getProject()).createParameterizedCachedValue(this, false); - ((PsiParameterizedCachedValue)cachedValue).setValue(result); - - } - else { - cachedValue = InjectedLanguageUtil.NULL_VALUE; - } - for (PsiElement operand : operands) { - operand.putUserData(INJECTED_PSI_IN_CONCATENATION, cachedValue); - } - anchor.putUserData(INJECTED_PSI_IN_CONCATENATION, cachedValue); - context.putUserData(INJECTED_PSI_IN_CONCATENATION, cachedValue); - - - return result; + Pair pair = computeAnchorAndOperands(context); + MultiHostRegistrarImpl registrar = doCompute(context, project, pair.first, pair.second); + return registrar == null ? null : CachedValueProvider.Result.create(registrar, PsiModificationTracker.MODIFICATION_COUNT, getInstance(project)); } } + private static Pair computeAnchorAndOperands(PsiElement context) { + PsiElement element = context; + PsiElement parent = context.getParent(); + while (parent instanceof PsiPolyadicExpression && ((PsiPolyadicExpression)parent).getOperationTokenType() == JavaTokenType.PLUS + || parent instanceof PsiAssignmentExpression && ((PsiAssignmentExpression)parent).getOperationTokenType() == JavaTokenType.PLUSEQ + || parent instanceof PsiConditionalExpression && ((PsiConditionalExpression)parent).getCondition() != element + || parent instanceof PsiTypeCastExpression + || parent instanceof PsiParenthesizedExpression) { + element = parent; + parent = parent.getParent(); + } + + PsiElement[] operands; + PsiElement anchor; + if (element instanceof PsiPolyadicExpression) { + operands = ((PsiPolyadicExpression)element).getOperands(); + anchor = element; + } + else if (element instanceof PsiAssignmentExpression) { + PsiExpression rExpression = ((PsiAssignmentExpression)element).getRExpression(); + operands = new PsiElement[]{rExpression == null ? element : rExpression}; + anchor = element; + } + else { + operands = new PsiElement[]{context}; + anchor = context; + } + + return Pair.create(anchor, operands); + } + private static MultiHostRegistrarImpl doCompute(PsiElement context, Project project, PsiElement anchor, PsiElement[] operands) { + MultiHostRegistrarImpl registrar = new MultiHostRegistrarImpl(project, context.getContainingFile(), anchor); + JavaConcatenationInjectorManager concatenationInjectorManager = getInstance(project); + for (ConcatenationAwareInjector concatenationInjector : concatenationInjectorManager.myConcatenationInjectors) { + concatenationInjector.getLanguagesToInject(registrar, operands); + if (registrar.result != null) break; + } + + if (registrar.result == null) { + registrar = null; + } + return registrar; + } + private static final Key> INJECTED_PSI_IN_CONCATENATION = Key.create("INJECTED_PSI_IN_CONCATENATION"); + private static final Key NO_CONCAT_INJECTION_TIMESTAMP = Key.create("NO_CONCAT_INJECTION_TIMESTAMP"); public static class Concatenation2InjectorAdapter implements MultiHostInjector { private final JavaConcatenationInjectorManager myManager; @@ -138,19 +135,52 @@ public class JavaConcatenationInjectorManager implements ModificationTracker { public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar, @NotNull PsiElement context) { if (myManager.myConcatenationInjectors.isEmpty()) return; - ParameterizedCachedValue cachedValue = context.getUserData(INJECTED_PSI_IN_CONCATENATION); + Project project = context.getProject(); + long modificationCount = PsiManager.getInstance(project).getModificationTracker().getModificationCount(); + Pair pair = computeAnchorAndOperands(context); + PsiElement anchor = pair.first; + PsiElement[] operands = pair.second; + Integer noInjectionTimestamp = anchor.getUserData(NO_CONCAT_INJECTION_TIMESTAMP); + MultiHostRegistrarImpl result; - if (cachedValue == null) { - CachedValueProvider.Result res = CONCATENATION_PSI_CACHED_VALUE_PROVIDER.compute(context); - result = res == null ? null : res.getValue(); + ParameterizedCachedValue data = null; + if (noInjectionTimestamp != null && noInjectionTimestamp == modificationCount) { + result = null; } else { - result = cachedValue.getValue(context); + data = anchor.getUserData(INJECTED_PSI_IN_CONCATENATION); + if (data == null) { + result = doCompute(context, project, anchor, operands); + + if (result != null) { + + } + } + else { + result = data.getValue(context); + } } if (result != null && result.result != null) { - for (Pair pair : result.result) { - ((MultiHostRegistrarImpl)registrar).addToResults(pair.first, pair.second); + for (Pair p : result.result) { + ((MultiHostRegistrarImpl)registrar).addToResults(p.first, p.second); } + + if (data == null) { + CachedValueProvider.Result cachedResult = + CachedValueProvider.Result.create(result, PsiModificationTracker.MODIFICATION_COUNT, getInstance(project)); + ParameterizedCachedValue cachedValue = + CachedValuesManager.getManager(context.getProject()) + .createParameterizedCachedValue(CONCATENATION_PSI_CACHED_VALUE_PROVIDER, false); + ((PsiParameterizedCachedValue)cachedValue).setValue(cachedResult); + + anchor.putUserData(INJECTED_PSI_IN_CONCATENATION, cachedValue); + anchor.putUserData(NO_CONCAT_INJECTION_TIMESTAMP, null); + } + } + else { + // cache no-injection flag + anchor.putUserData(INJECTED_PSI_IN_CONCATENATION, null); + anchor.putUserData(NO_CONCAT_INJECTION_TIMESTAMP, (int)modificationCount); } } @@ -160,30 +190,6 @@ public class JavaConcatenationInjectorManager implements ModificationTracker { } } - private static void collectOperands(PsiExpression expression, final List operands) { - expression.accept(new JavaRecursiveElementWalkingVisitor() { - @Override - public void visitPolyadicExpression(PsiPolyadicExpression expression) { - super.visitElement(expression); - } - - @Override - public void visitExpression(PsiExpression expression) { - operands.add(expression); // add leaf expression to the operand list - } - - @Override - public void visitReferenceExpression(PsiReferenceExpression expression) { - visitExpression(expression); - } - - @Override - public void visitElement(PsiElement element) { - // do not go deeper - } - }); - } - private final List myConcatenationInjectors = ContainerUtil.createEmptyCOWList(); public void registerConcatenationInjector(@NotNull ConcatenationAwareInjector injector) { myConcatenationInjectors.add(injector); diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/injected/MyTestInjector.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/injected/MyTestInjector.java index a10ddafcd5b2..8f8bd52eb13c 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/injected/MyTestInjector.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/injected/MyTestInjector.java @@ -101,11 +101,11 @@ public class MyTestInjector { } private static void registerForStringVarInitializer(@NotNull Disposable parent, - @NotNull final Project project, - final Language language, - @NotNull @NonNls final String varName, - @NonNls final String prefix, - @NonNls final String suffix) { + @NotNull final Project project, + final Language language, + @NotNull @NonNls final String varName, + @NonNls final String prefix, + @NonNls final String suffix) { if (language == null) return; final ConcatenationAwareInjector injector = new ConcatenationAwareInjector() { public void getLanguagesToInject(@NotNull MultiHostRegistrar injectionPlacesRegistrar, @NotNull PsiElement... operands) { diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiPolyadicExpressionImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiPolyadicExpressionImpl.java index af90b3a334d8..4b1dfe2c00bd 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiPolyadicExpressionImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiPolyadicExpressionImpl.java @@ -116,9 +116,19 @@ public class PsiPolyadicExpressionImpl extends ExpressionPsiElement implements P @NotNull @Override public PsiExpression[] getOperands() { - return getChildrenAsPsiElements(ElementType.EXPRESSION_BIT_SET, Constants.PSI_EXPRESSION_ARRAY_CONSTRUCTOR); + PsiExpression[] operands = cachedOperands; + if (operands == null) { + cachedOperands = operands = getChildrenAsPsiElements(ElementType.EXPRESSION_BIT_SET, Constants.PSI_EXPRESSION_ARRAY_CONSTRUCTOR); + } + return operands; } + private volatile PsiExpression[] cachedOperands; + @Override + public void clearCaches() { + cachedOperands = null; + super.clearCaches(); + } public String toString() { return "PsiPolyadicExpression: " + getText(); diff --git a/java/testFramework/src/com/intellij/codeInsight/daemon/LightDaemonAnalyzerTestCase.java b/java/testFramework/src/com/intellij/codeInsight/daemon/LightDaemonAnalyzerTestCase.java index 1be48ad506ec..478e28156ba2 100644 --- a/java/testFramework/src/com/intellij/codeInsight/daemon/LightDaemonAnalyzerTestCase.java +++ b/java/testFramework/src/com/intellij/codeInsight/daemon/LightDaemonAnalyzerTestCase.java @@ -19,6 +19,7 @@ import com.intellij.codeHighlighting.Pass; import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl; import com.intellij.codeInsight.daemon.impl.HighlightInfo; import com.intellij.injected.editor.EditorWindow; +import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.vfs.VirtualFileFilter; @@ -108,6 +109,10 @@ public abstract class LightDaemonAnalyzerTestCase extends LightCodeInsightTestCa return CodeInsightTestFixtureImpl.instantiateAndRun(file, editor, toIgnore, false); } + protected List doHighlighting(HighlightSeverity minSeverity) { + return DaemonAnalyzerTestCase.filter(doHighlighting(), minSeverity); + } + protected boolean doFolding() { return false; } diff --git a/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java b/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java index 284a541e5c72..c9a3f4ca3f36 100644 --- a/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java +++ b/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java @@ -148,21 +148,23 @@ public class EditorWindow extends UserDataHolderBase implements EditorEx { int column = pos.column; int lineStartOffset = myDocumentWindow.getLineStartOffset(pos.line); int lineEndOffset = myDocumentWindow.getLineEndOffset(pos.line); - int delta; - int baseOffset; if (column > lineEndOffset - lineStartOffset) { - // in virtual space, calculate offset based on a line end - delta = column - (lineEndOffset - lineStartOffset); - baseOffset = lineEndOffset; + // in virtual space, calculate the host pos as an offset from the line end + int delta = column - (lineEndOffset - lineStartOffset); + + int baseOffsetInHost = myDocumentWindow.injectedToHost(lineEndOffset); + LogicalPosition lineStartPosInHost = myDelegate.offsetToLogicalPosition(baseOffsetInHost); + return new LogicalPosition(lineStartPosInHost.line, lineStartPosInHost.column + delta); } else { - delta = column; - baseOffset = lineStartOffset; + int offset = lineStartOffset + column; + int hostOffset = getDocument().injectedToHost(offset); + int hostLineNumber = getDocument().getDelegate().getLineNumber(hostOffset); + int hostLineStart = getDocument().getDelegate().getLineStartOffset(hostLineNumber); + + return new LogicalPosition(hostLineNumber, hostOffset - hostLineStart); } - int baseOffsetInHost = myDocumentWindow.injectedToHost(baseOffset); - LogicalPosition lineStartPosInHost = myDelegate.offsetToLogicalPosition(baseOffsetInHost); - return new LogicalPosition(lineStartPosInHost.line, lineStartPosInHost.column + delta); } private void dispose() { 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 7e6fc1ada480..92d6608366a8 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 @@ -37,7 +37,6 @@ import com.intellij.psi.impl.PsiParameterizedCachedValue; import com.intellij.psi.impl.source.DummyHolder; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.*; -import com.intellij.util.ParameterizedCachedValueImpl; import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -187,53 +186,29 @@ public class InjectedLanguageUtil { } private static final InjectedPsiCachedValueProvider INJECTED_PSI_PROVIDER = new InjectedPsiCachedValueProvider(); - private static final Key> INJECTED_PSI_KEY = Key.create("INJECTED_PSI"); - public static ParameterizedCachedValue NULL_VALUE = new ParameterizedCachedValueImpl(new ParameterizedCachedValueProvider() { - @Override - public CachedValueProvider.Result compute(PsiElement param) { - return CachedValueProvider.Result.create(NULL_REGISTRAR, param.getManager().getModificationTracker()); - } - }) { - @Override - public boolean isFromMyProject(Project project) { - return true; - } - }; - - private static final MultiHostRegistrarImpl NULL_REGISTRAR = new MultiHostRegistrarImpl(); - + private static final Key> INJECTED_PSI = Key.create("INJECTED_PSI"); private static MultiHostRegistrarImpl probeElementsUp(@NotNull PsiElement element, @NotNull PsiFile hostPsiFile, boolean probeUp) { PsiManager psiManager = hostPsiFile.getManager(); final Project project = psiManager.getProject(); InjectedLanguageManagerImpl injectedManager = InjectedLanguageManagerImpl.getInstanceImpl(project); if (injectedManager == null) return null; //for tests - + long modificationCount = psiManager.getModificationTracker().getModificationCount(); MultiHostRegistrarImpl registrar = null; - PsiElement current; + PsiElement current = element; nextParent: - for (current = element; current != null && current != hostPsiFile; current = current.getParent()) { + while (current != null && current != hostPsiFile) { ProgressManager.checkCanceled(); if ("EL".equals(current.getLanguage().getID())) break; - ParameterizedCachedValue data = current.getUserData(INJECTED_PSI_KEY); + ParameterizedCachedValue data = current.getUserData(INJECTED_PSI); if (data == null) { registrar = InjectedPsiCachedValueProvider.doCompute(current, injectedManager, project, hostPsiFile); - if (registrar != null) { - ParameterizedCachedValue cachedValue = - CachedValuesManager.getManager(project).createParameterizedCachedValue(INJECTED_PSI_PROVIDER, false); - - CachedValueProvider.Result result = CachedValueProvider.Result.create(registrar, PsiModificationTracker.MODIFICATION_COUNT, calcDependencies(registrar)); - ((PsiParameterizedCachedValue)cachedValue).setValue(result); - current.putUserData(INJECTED_PSI_KEY, cachedValue); - } } else { registrar = data.getValue(current); } - if (registrar == NULL_REGISTRAR) { - registrar = null; - break; - } + current = current.getParent(); // cache no injection for current + if (registrar != null) { List> places = registrar.result; // check that injections found intersect with queried element @@ -247,13 +222,27 @@ public class InjectedLanguageUtil { } } } - if (!probeUp) break; + if (!probeUp) { + break; + } } - if (registrar == null) { + + if (probeUp) { + // cache only if we walked all parents for (PsiElement e = element; e != current && e != null && e != hostPsiFile; e = e.getParent()) { ProgressManager.checkCanceled(); - //store no-injection flag - e.putUserData(INJECTED_PSI_KEY, NULL_VALUE); + if (registrar == null) { + e.putUserData(INJECTED_PSI, null); + } + else { + ParameterizedCachedValue cachedValue = + CachedValuesManager.getManager(project).createParameterizedCachedValue(INJECTED_PSI_PROVIDER, false); + + CachedValueProvider.Result result = CachedValueProvider.Result.create(registrar, PsiModificationTracker.MODIFICATION_COUNT, calcDependencies(registrar)); + ((PsiParameterizedCachedValue)cachedValue).setValue(result); + + e.putUserData(INJECTED_PSI, cachedValue); + } } } return registrar; diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/MultiHostRegistrarImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/MultiHostRegistrarImpl.java index bc2a2d573d2e..eb92d6b56472 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/MultiHostRegistrarImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/MultiHostRegistrarImpl.java @@ -95,6 +95,11 @@ public class MultiHostRegistrarImpl implements MultiHostRegistrar { return result; } + @NotNull + public PsiElement getContextElement() { + return myContextElement; + } + @NotNull public MultiHostRegistrar startInjecting(@NotNull Language language) { escapers = new SmartList>();