[groovy] do not increment Java Structure modification count in method
authorDaniil Ovchinnikov <daniil.ovchinnikov@jetbrains.com>
Wed, 16 Nov 2016 18:42:39 +0000 (21:42 +0300)
committerDaniil Ovchinnikov <daniil.ovchinnikov@jetbrains.com>
Wed, 16 Nov 2016 20:04:03 +0000 (23:04 +0300)
and script bodies

plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyFileImpl.java
plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/blocks/GrOpenBlockImpl.java
plugins/groovy/test/org/jetbrains/plugins/groovy/lang/psi/GroovyModificationTrackersTest.groovy [new file with mode: 0644]
plugins/groovy/test/org/jetbrains/plugins/groovy/transformations/BaseScriptTransformationSupportTest.groovy

index 70ac76f3210dd17a6734bb610ce2f2720a32a044..39a7cbce58fba34e07b5fdd8d144603d0f38483e 100644 (file)
@@ -70,7 +70,8 @@ import java.util.concurrent.ConcurrentMap;
  *
  * @author ilyas
  */
-public class GroovyFileImpl extends GroovyFileBaseImpl implements GroovyFile {
+public class GroovyFileImpl extends GroovyFileBaseImpl implements GroovyFile, PsiModifiableCodeBlock {
+
   private static final Logger LOG = Logger.getInstance("org.jetbrains.plugins.groovy.lang.psi.impl.GroovyFileImpl");
 
   private static final String SYNTHETIC_PARAMETER_NAME = "args";
@@ -439,7 +440,8 @@ public class GroovyFileImpl extends GroovyFileBaseImpl implements GroovyFile {
     if (currentPackage != null) {
       final ASTNode currNode = currentPackage.getNode();
       fileNode.replaceChild(currNode, newNode);
-    } else {
+    }
+    else {
       ASTNode anchor = fileNode.getFirstChildNode();
       if (anchor != null && anchor.getElementType() == GroovyTokenTypes.mSH_COMMENT) {
         anchor = anchor.getTreeNext();
@@ -542,8 +544,17 @@ public class GroovyFileImpl extends GroovyFileBaseImpl implements GroovyFile {
   }
 
   @Override
+  public boolean shouldChangeModificationCount(PsiElement place) {
+    // 1. We actually should never get GrTypeDefinion as a parent, because it is a PsiClass,
+    //    and PsiClasses prevent to go up in a tree any further
+    // 2. If place is under a variable then @BaseScript or @Field may be changed,
+    //    which actually is a change in Java Structure
+    return !isScript() || PsiTreeUtil.getParentOfType(place, GrTypeDefinition.class, GrVariableDeclaration.class) != null;
+  }
+
+  @Override
   public String toString() {
-    if (ApplicationManager.getApplication().isUnitTestMode()){
+    if (ApplicationManager.getApplication().isUnitTestMode()) {
       return super.toString();
     }
     return "GroovyFileImpl:" + getName();
index dfe65a996e17ce0ebe5e749b75be698be5649c71..63a13c98b09213c641fdb23a02f9088b517fb20e 100644 (file)
@@ -17,6 +17,7 @@
 package org.jetbrains.plugins.groovy.lang.psi.impl.statements.blocks;
 
 import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiModifiableCodeBlock;
 import com.intellij.psi.tree.IElementType;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor;
@@ -27,7 +28,8 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMe
 /**
  * @author ilyas
  */
-public class GrOpenBlockImpl extends GrBlockImpl implements GrOpenBlock {
+public class GrOpenBlockImpl extends GrBlockImpl implements GrOpenBlock, PsiModifiableCodeBlock {
+
   public GrOpenBlockImpl(@NotNull IElementType type, CharSequence buffer) {
     super(type, buffer);
   }
@@ -46,4 +48,9 @@ public class GrOpenBlockImpl extends GrBlockImpl implements GrOpenBlock {
     final PsiElement parent = getParent();
     return parent instanceof GrMethod || parent instanceof GrClassInitializer;
   }
+
+  @Override
+  public boolean shouldChangeModificationCount(PsiElement place) {
+    return !isTopControlFlowOwner();
+  }
 }
diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/psi/GroovyModificationTrackersTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/psi/GroovyModificationTrackersTest.groovy
new file mode 100644 (file)
index 0000000..551d696
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.plugins.groovy.lang.psi
+
+import com.intellij.psi.PsiDocumentManager
+import com.intellij.testFramework.LightProjectDescriptor
+import groovy.transform.CompileStatic
+import groovy.transform.TypeCheckingMode
+import org.jetbrains.plugins.groovy.GroovyLightProjectDescriptor
+import org.jetbrains.plugins.groovy.LightGroovyTestCase
+
+@CompileStatic
+class GroovyModificationTrackersTest extends LightGroovyTestCase {
+
+  LightProjectDescriptor projectDescriptor = GroovyLightProjectDescriptor.GROOVY_LATEST
+
+  void 'test class method'() {
+    doTest '''\
+class A {
+  def foo() { <caret> } 
+}
+''', false
+  }
+
+  void 'test class initializer'() {
+    doTest '''\
+class A {
+ {
+   <caret>
+ }
+}
+''', false
+  }
+
+  void 'test class body'() {
+    doTest '''\
+class A {
+  <caret>
+}
+''', true
+  }
+
+  void 'test script body'() {
+    doTest '<caret>', false
+  }
+
+  void 'test script variable'() {
+    doTest 'def a<caret>= 1', true
+  }
+
+  @CompileStatic(TypeCheckingMode.SKIP)
+  void doTest(String text, boolean shouldChange) {
+    fixture.configureByText '_.groovy', text
+    def (beforeStructure, beforeOutOfCodeBlock) = getModificationCounts()
+    fixture.type " "
+    PsiDocumentManager.getInstance(project).commitDocument(editor.document)
+    def (afterStructure, afterOutOfCodeBlock) = getModificationCounts()
+    if (shouldChange) {
+      assert beforeStructure < afterStructure
+      assert beforeOutOfCodeBlock < afterOutOfCodeBlock
+    }
+    else {
+      assert beforeStructure == afterStructure
+      assert beforeOutOfCodeBlock == afterOutOfCodeBlock
+    }
+  }
+
+  @CompileStatic
+  List<Long> getModificationCounts() {
+    psiManager.modificationTracker.with {
+      [javaStructureModificationCount, outOfCodeBlockModificationCount]
+    }
+  }
+}
index 4e2f57a35599c659db68bb17302423f0aa1bf90e..da0c653327d5fae2dd3b939c4678c79dcc15420d 100644 (file)
@@ -41,8 +41,12 @@ import groovy.transform.BaseScript
 
 $text
 """) as GroovyFileImpl
+    file.treeElementPointer = null
+
     def clazz = fixture.findClass('Zzz')
     assert clazz instanceof GroovyScriptClass
+    assert !file.contentsLoaded
+
     assert InheritanceUtil.isInheritor(clazz as PsiClass, 'MyBaseScript')
     assert !file.contentsLoaded
   }