diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 1d83d95d8bc..6fd690eba51 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -59,7 +59,9 @@ - + + + diff --git a/idea/src/org/jetbrains/jet/plugin/java/JetCodeBlockModificationListener.java b/idea/src/org/jetbrains/jet/plugin/java/JetCodeBlockModificationListener.java new file mode 100644 index 00000000000..dd26ab448c4 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/java/JetCodeBlockModificationListener.java @@ -0,0 +1,112 @@ +/* + * @author max + */ +package org.jetbrains.jet.plugin.java; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.psi.*; +import com.intellij.psi.impl.PsiModificationTrackerImpl; +import com.intellij.psi.impl.PsiTreeChangeEventImpl; +import com.intellij.psi.impl.PsiTreeChangePreprocessor; +import com.intellij.psi.util.PsiModificationTracker; +import org.jetbrains.jet.lang.psi.JetBlockExpression; +import org.jetbrains.jet.lang.psi.JetClass; +import org.jetbrains.jet.lang.psi.JetFile; + +public class JetCodeBlockModificationListener implements PsiTreeChangePreprocessor { + private static final Logger LOG = Logger.getInstance("#org.jetbrains.jet.plugin.java.JetCodeBlockModificationListener"); + + private final PsiModificationTrackerImpl myModificationTracker; + + public JetCodeBlockModificationListener(final PsiModificationTracker modificationTracker) { + myModificationTracker = (PsiModificationTrackerImpl) modificationTracker; + } + + @Override + public void treeChanged(final PsiTreeChangeEventImpl event) { + switch (event.getCode()) { + case BEFORE_CHILDREN_CHANGE: + case BEFORE_PROPERTY_CHANGE: + case BEFORE_CHILD_MOVEMENT: + case BEFORE_CHILD_REPLACEMENT: + case BEFORE_CHILD_ADDITION: + case BEFORE_CHILD_REMOVAL: + break; + + case CHILD_ADDED: + case CHILD_REMOVED: + case CHILD_REPLACED: + processChange(event.getParent(), event.getOldChild(), event.getChild()); + break; + + case CHILDREN_CHANGED: + // general childrenChanged() event after each change + if (!event.isGenericChildrenChange()) { + processChange(event.getParent(), event.getParent(), null); + } + break; + + case CHILD_MOVED: + case PROPERTY_CHANGED: + myModificationTracker.incCounter(); + break; + + default: + LOG.error("Unknown code:" + event.getCode()); + break; + } + } + + private void processChange(final PsiElement parent, final PsiElement child1, final PsiElement child2) { + try { + if (!isInsideCodeBlock(parent)) { + if (parent != null && parent.getContainingFile() instanceof JetFile) { + myModificationTracker.incCounter(); + } + else { + myModificationTracker.incOutOfCodeBlockModificationCounter(); + } + return; + } + + if (containsClassesInside(child1) || child2 != child1 && containsClassesInside(child2)) { + myModificationTracker.incCounter(); + } + } catch (PsiInvalidElementAccessException e) { + myModificationTracker.incCounter(); // Shall not happen actually, just a pre-release paranoia + } + } + + private static boolean containsClassesInside(final PsiElement element) { + if (element == null) return false; + if (element instanceof PsiClass) return true; + + PsiElement child = element.getFirstChild(); + while (child != null) { + if (containsClassesInside(child)) return true; + child = child.getNextSibling(); + } + + return false; + } + + private static boolean isInsideCodeBlock(PsiElement element) { + if (element instanceof PsiFileSystemItem) { + return false; + } + + if (element == null || element.getParent() == null) return true; + + PsiElement parent = element; + while (true) { + if (parent instanceof PsiFile || parent instanceof PsiDirectory || parent == null) { + return false; + } + if (parent instanceof JetClass) return false; // anonymous or local class + if (parent instanceof JetBlockExpression) { + return true; + } + parent = parent.getParent(); + } + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/java/JetLightClass.java b/idea/src/org/jetbrains/jet/plugin/java/JetLightClass.java index bff50a2487e..3d992837782 100644 --- a/idea/src/org/jetbrains/jet/plugin/java/JetLightClass.java +++ b/idea/src/org/jetbrains/jet/plugin/java/JetLightClass.java @@ -15,6 +15,10 @@ import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl; import com.intellij.psi.impl.light.AbstractLightClass; import com.intellij.psi.stubs.PsiClassHolderFileStub; import com.intellij.psi.stubs.StubElement; +import com.intellij.psi.util.CachedValue; +import com.intellij.psi.util.CachedValueProvider; +import com.intellij.psi.util.CachedValuesManager; +import com.intellij.psi.util.PsiModificationTracker; import com.intellij.util.containers.Stack; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.codegen.ClassBuilder; @@ -33,7 +37,7 @@ import java.util.List; public class JetLightClass extends AbstractLightClass implements JetJavaMirrorMarker { private static final Logger LOG = Logger.getInstance("#org.jetbrains.jet.plugin.java.JetLightClass"); - private final static Key JAVA_API_STUB = Key.create("JAVA_API_STUB"); + private final static Key> JAVA_API_STUB = Key.create("JAVA_API_STUB"); private final JetFile file; private final String className; @@ -83,13 +87,18 @@ public class JetLightClass extends AbstractLightClass implements JetJavaMirrorMa } private PsiJavaFileStub getStub() { - PsiJavaFileStub answer = file.getUserData(JAVA_API_STUB); + CachedValue answer = file.getUserData(JAVA_API_STUB); if (answer == null) { - answer = calcStub(); + answer = CachedValuesManager.getManager(getProject()).createCachedValue(new CachedValueProvider() { + @Override + public Result compute() { + return Result.create(calcStub(), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT); + } + }, false); file.putUserData(JAVA_API_STUB, answer); } - return answer; + return answer.getValue(); } private PsiJavaFileStub calcStub() { @@ -139,7 +148,7 @@ public class JetLightClass extends AbstractLightClass implements JetJavaMirrorMa finally { final StubElement pop = stubStack.pop(); if (pop != answer) { - LOG.error("Unbalanced stack operations"); + LOG.error("Unbalanced stack operations: " + pop); } } }