diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/geb/GebContentDeclarationSearcher.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/geb/GebContentDeclarationSearcher.java index 8c9a3c4d2068..c7dd73c4f543 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/geb/GebContentDeclarationSearcher.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/geb/GebContentDeclarationSearcher.java @@ -1,25 +1,11 @@ -/* - * Copyright 2000-2017 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. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.plugins.groovy.geb; import com.intellij.pom.PomDeclarationSearcher; import com.intellij.pom.PomTarget; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiField; +import com.intellij.psi.PsiMember; import com.intellij.psi.PsiModifier; import com.intellij.psi.util.InheritanceUtil; import com.intellij.util.Consumer; @@ -52,11 +38,11 @@ public class GebContentDeclarationSearcher extends PomDeclarationSearcher { if (!InheritanceUtil.isInheritor(containingClass, "geb.Page") && !InheritanceUtil.isInheritor(containingClass, "geb.Module")) return; - Map elements = GebUtil.getContentElements(containingClass); + Map contentElements = GebUtil.getContentElements(containingClass); - for (PsiField f : elements.values()) { - if (f.getNavigationElement() == element) { - consumer.consume(f); + for (PsiMember contentElement : contentElements.values()) { + if (contentElement.getNavigationElement() == element) { + consumer.consume(contentElement); return; } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/geb/GebPageMemberContributor.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/geb/GebPageMemberContributor.java index 140b195b8f86..2253e7be2a39 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/geb/GebPageMemberContributor.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/geb/GebPageMemberContributor.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2015 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. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.plugins.groovy.geb; import com.intellij.psi.*; @@ -54,10 +40,10 @@ public class GebPageMemberContributor extends NonCodeMembersContributor { if (contentField instanceof GrField) { GrField f = (GrField)contentField; if ("content".equals(f.getName()) && f.hasModifierProperty(PsiModifier.STATIC) && f.getContainingClass() == aClass) { - Map elements = GebUtil.getContentElements(aClass); - for (PsiField field : elements.values()) { - if (field.getNavigationElement() == place) { - return; // Don't resolve variable definition. + Map elements = GebUtil.getContentElements(aClass); + for (PsiMember element : elements.values()) { + if (element.getNavigationElement() == place) { + return; // Don't resolve definition. } } } @@ -65,27 +51,27 @@ public class GebPageMemberContributor extends NonCodeMembersContributor { } } - processPageFields(processor, aClass, state); + processPageElements(processor, aClass, state); } - public static boolean processPageFields(PsiScopeProcessor processor, - @NotNull PsiClass pageClass, - ResolveState state) { + public static boolean processPageElements(PsiScopeProcessor processor, + @NotNull PsiClass pageClass, + ResolveState state) { Map supers = ClassUtil.getSuperClassesWithCache(pageClass); String nameHint = ResolveUtil.getNameHint(processor); for (PsiClass psiClass : supers.values()) { - Map contentFields = GebUtil.getContentElements(psiClass); + Map contentElements = GebUtil.getContentElements(psiClass); if (nameHint == null) { - for (Map.Entry entry : contentFields.entrySet()) { + for (Map.Entry entry : contentElements.entrySet()) { if (!processor.execute(entry.getValue(), state)) return false; } } else { - PsiField field = contentFields.get(nameHint); - if (field != null) { - return processor.execute(field, state); + PsiMember element = contentElements.get(nameHint); + if (element != null) { + return processor.execute(element, state); } } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/geb/GebUtil.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/geb/GebUtil.java index 79cbbe11b38e..62bb88de8fc5 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/geb/GebUtil.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/geb/GebUtil.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2017 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. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.plugins.groovy.geb; import com.intellij.psi.*; @@ -25,7 +11,9 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrCall; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightField; +import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightMethodBuilder; import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; import org.jetbrains.plugins.groovy.util.LightCacheKey; @@ -38,7 +26,7 @@ import java.util.Map; */ public class GebUtil { - private static final LightCacheKey> KEY = LightCacheKey.createByFileModificationCount(); + private static final LightCacheKey> KEY = LightCacheKey.createByFileModificationCount(); public static boolean contributeMembersInsideTest(PsiScopeProcessor processor, PsiElement place, @@ -59,8 +47,8 @@ public class GebUtil { return true; } - public static Map getContentElements(@NotNull PsiClass pageOrModuleClass) { - Map res = KEY.getCachedValue(pageOrModuleClass); + public static Map getContentElements(@NotNull PsiClass pageOrModuleClass) { + Map res = KEY.getCachedValue(pageOrModuleClass); if (res == null) { res = calculateContentElements(pageOrModuleClass); res = KEY.putCachedValue(pageOrModuleClass, res); @@ -69,7 +57,7 @@ public class GebUtil { return res; } - private static Map calculateContentElements(@NotNull PsiClass pageOrModuleClass) { + private static Map calculateContentElements(@NotNull PsiClass pageOrModuleClass) { PsiField contentField = pageOrModuleClass.findFieldByName("content", false); if (!(contentField instanceof GrField)) return Collections.emptyMap(); @@ -77,7 +65,7 @@ public class GebUtil { GrExpression initializer = ((GrField)contentField).getInitializerGroovy(); if (!(initializer instanceof GrClosableBlock)) return Collections.emptyMap(); - Map res = new HashMap<>(); + Map res = new HashMap<>(); PsiType objectType = PsiType.getJavaLangObject(pageOrModuleClass.getManager(), pageOrModuleClass.getResolveScope()); for (PsiElement e = initializer.getFirstChild(); e != null; e = e.getNextSibling()) { @@ -87,6 +75,8 @@ public class GebUtil { GrExpression invokedExpression = methodCall.getInvokedExpression(); if (!(invokedExpression instanceof GrReferenceExpression)) continue; if (((GrReferenceExpression)invokedExpression).isQualified()) continue; + String name = ((GrReferenceExpression)invokedExpression).getReferenceName(); + if (name == null) continue; GrExpression[] arguments = PsiUtil.getAllArguments((GrCall)e); if (arguments.length == 0) continue; @@ -102,24 +92,58 @@ public class GebUtil { continue; } - GrLightField field = new GrLightField(pageOrModuleClass, ((GrReferenceExpression)invokedExpression).getReferenceName(), objectType, invokedExpression) { - @Override - public PsiType getTypeGroovy() { - return block.getReturnType(); - } - - @Override - public PsiType getDeclaredType() { - return null; - } - }; - - field.getModifierList().addModifier(GrModifierFlags.STATIC_MASK); - - res.put(field.getName(), field); + PsiMember target; + if (block.hasParametersSection()) { + target = extractMethodForContent(pageOrModuleClass, name, invokedExpression, block); + } + else { + target = extractFieldForContent(pageOrModuleClass, objectType, name, invokedExpression, block); + } + res.put(name, target); } } return res; } + + @NotNull + private static PsiField extractFieldForContent(@NotNull PsiClass pageOrModuleClass, + @NotNull PsiType objectType, String name, + @NotNull GrExpression invokedExpression, + @NotNull GrClosableBlock block) { + GrLightField field = new GrLightField(pageOrModuleClass, name, objectType, invokedExpression) { + + @Override + public PsiType getTypeGroovy() { + return block.getReturnType(); + } + + @Override + public PsiType getDeclaredType() { + return null; + } + }; + field.getModifierList().addModifier(GrModifierFlags.STATIC_MASK); + return field; + } + + @NotNull + private static PsiMethod extractMethodForContent(@NotNull PsiClass pageOrModuleClass, + @NotNull String name, + @NotNull GrExpression invokedExpression, + @NotNull GrClosableBlock block) { + GrLightMethodBuilder method = new GrLightMethodBuilder(pageOrModuleClass.getManager(), name) { + @Override + public PsiType getReturnType() { + return block.getReturnType(); + } + }; + method.setContainingClass(pageOrModuleClass); + method.addModifier(GrModifierFlags.STATIC_MASK); + method.setNavigationElement(invokedExpression); + for (GrParameter parameter : block.getAllParameters()) { + method.addParameter(parameter); + } + return method; + } } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/geb/GebTestsTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/geb/GebTestsTest.groovy index 81008c72449b..a5624b8557f0 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/geb/GebTestsTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/geb/GebTestsTest.groovy @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2013 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. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.plugins.groovy.geb import org.jetbrains.plugins.groovy.codeInspection.assignment.GroovyAssignabilityCheckInspection @@ -92,6 +78,39 @@ class ParentClass extends geb.Page { TestUtils.checkCompletionContains(myFixture, "allElements()", "add()", "firstElement()") } + void testResolveContentFieldsAndMethods() { + myFixture.configureByText("PageWithContent.groovy", """ +class PageWithContent extends geb.Page { + static content = { + button { \$('button') } + formField { String name -> \$('input', name: name) } + } + + def someMethod() { + + } +} +""") + + TestUtils.checkCompletionContains(myFixture, "button", "formField()") + } + + void testContentMethodReturnType() { + myFixture.configureByText("PageWithContent.groovy", """ +class PageWithContent extends geb.Page { + static content = { + formField { String name -> \$('input', name: name) } + } + + def someMethod() { + formField('username'). + } +} +""") + + TestUtils.checkCompletionContains(myFixture, "allElements()", "add()", "firstElement()") + } + void testCheckHighlighting() { myFixture.enableInspections(GroovyAssignabilityCheckInspection)