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);
}
}
}