diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml
index 72d1233a2d96..44e1faf56d4a 100644
--- a/plugins/groovy/src/META-INF/plugin.xml
+++ b/plugins/groovy/src/META-INF/plugin.xml
@@ -371,6 +371,8 @@
+
+
getNames() {
+ assert !StringUtil.isEmptyOrSpaces(names);
+ return StringUtil.tokenize(names, ATTR_NAMES_DELIMITER);
+ }
}
@Nullable
@@ -92,6 +100,23 @@ public class GroovyMethodDescriptor {
}
}
+ public Map getNamedArgumentsReferenceProviders() {
+ if (myArguments == null) return Collections.emptyMap();
+
+ Map res = new HashMap();
+
+ for (NamedArguments argument : myArguments) {
+ if (argument.referenceProvider != null) {
+ for (String name : argument.getNames()) {
+ Object oldValue = res.put(name, argument.referenceProvider);
+ assert oldValue == null;
+ }
+ }
+ }
+
+ return res;
+ }
+
@Nullable
public Map getArgumentsMap() {
if (myArguments == null && namedArgs == null) {
@@ -108,13 +133,7 @@ public class GroovyMethodDescriptor {
for (NamedArguments arguments : myArguments) {
NamedArgumentDescriptor descriptor = getDescriptor(isNamedArgsShowFirst, arguments.isFirst, arguments.type);
- assert !StringUtil.isEmptyOrSpaces(arguments.names);
-
- String names = arguments.names;
-
- for (StringTokenizer st = new StringTokenizer(names, ATTR_NAMES_DELIMITER); st.hasMoreTokens(); ) {
- String name = st.nextToken();
-
+ for (String name : arguments.getNames()) {
Object oldValue = res.put(name, descriptor);
assert oldValue == null;
}
diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/extensions/GroovyMethodInfo.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/extensions/GroovyMethodInfo.java
index 9b9a10f31202..c77a16419b2f 100644
--- a/plugins/groovy/src/org/jetbrains/plugins/groovy/extensions/GroovyMethodInfo.java
+++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/extensions/GroovyMethodInfo.java
@@ -1,7 +1,9 @@
package org.jetbrains.plugins.groovy.extensions;
+import com.intellij.openapi.application.ApplicationManager;
import com.intellij.psi.*;
import com.intellij.util.PairFunction;
+import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall;
@@ -19,7 +21,9 @@ public class GroovyMethodInfo {
private static volatile Map>> METHOD_INFOS;
private static Map>> LIGHT_METHOD_INFOS;
-
+
+ private static final Set myAllSupportedNamedArguments = new HashSet();
+
private final List myParams;
private final ClassLoader myClassLoader;
@@ -28,9 +32,11 @@ public class GroovyMethodInfo {
private PairFunction myReturnTypeCalculatorInstance;
private final Map myNamedArguments;
- private final String myNamedArgProviderClassName;
+ private final String myNamedArgProviderClassName;
private GroovyNamedArgumentProvider myNamedArgProviderInstance;
+ private Map myNamedArgReferenceProviderClassNames;
+
private static void ensureInit() {
if (METHOD_INFOS != null) return;
@@ -92,15 +98,14 @@ public class GroovyMethodInfo {
ensureInit();
List lightMethodInfos = null;
- if (method instanceof GrLightMethodBuilder) {
- Object methodKind = ((GrLightMethodBuilder)method).getMethodKind();
- if (methodKind instanceof String) {
- lightMethodInfos = getInfos(LIGHT_METHOD_INFOS, (String)methodKind, method);
- }
+
+ Object methodKind = GrLightMethodBuilder.getMethodKind(method);
+ if (methodKind instanceof String) {
+ lightMethodInfos = getInfos(LIGHT_METHOD_INFOS, (String)methodKind, method);
}
List methodInfos = null;
-
+
PsiClass containingClass = method.getContainingClass();
if (containingClass != null) {
methodInfos = getInfos(METHOD_INFOS, containingClass.getQualifiedName(), method);
@@ -114,10 +119,7 @@ public class GroovyMethodInfo {
return methodInfos;
}
else {
- List res = new ArrayList(lightMethodInfos.size() + methodInfos.size());
- res.addAll(lightMethodInfos);
- res.addAll(methodInfos);
- return res;
+ return ContainerUtil.concat(lightMethodInfos, methodInfos);
}
}
}
@@ -132,6 +134,31 @@ public class GroovyMethodInfo {
myNamedArguments = method.getArgumentsMap();
myNamedArgProviderClassName = method.namedArgsProvider;
assert myNamedArguments == null || myNamedArgProviderClassName == null;
+
+ myNamedArgReferenceProviderClassNames = method.getNamedArgumentsReferenceProviders();
+
+ myAllSupportedNamedArguments.addAll(myNamedArgReferenceProviderClassNames.keySet());
+
+ if (ApplicationManager.getApplication().isInternal()) {
+ // Check classes to avoid typo.
+
+ try {
+ if (myReturnTypeCalculatorClassName != null) {
+ classLoader.loadClass(myReturnTypeCalculatorClassName);
+ }
+ if (myNamedArgProviderClassName != null) {
+ classLoader.loadClass(myNamedArgProviderClassName);
+ }
+
+ for (String className : myNamedArgReferenceProviderClassNames.values()) {
+ Class> aClass = classLoader.loadClass(className);
+ assert PsiReferenceProvider.class.isAssignableFrom(aClass) || GroovyNamedArgumentReferenceProvider.class.isAssignableFrom(aClass);
+ }
+ }
+ catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
private static void addMethodDescriptor(Map>> res,
@@ -187,6 +214,21 @@ public class GroovyMethodInfo {
return myReturnTypeCalculatorInstance;
}
+ public static Set getAllSupportedNamedArguments() {
+ return myAllSupportedNamedArguments;
+ }
+
+ /**
+ * @return instance of PsiReferenceProvider or GroovyNamedArgumentReferenceProvider or null.
+ */
+ @Nullable
+ public Object getNamedArgReferenceProvider(String namedArgumentName) {
+ String className = myNamedArgReferenceProviderClassNames.get(namedArgumentName);
+ if (className == null) return null;
+
+ return ClassInstanceCache.getInstance(className, myClassLoader);
+ }
+
@Nullable
public Map getNamedArguments() {
return myNamedArguments;
diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/extensions/GroovyNamedArgumentReferenceProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/extensions/GroovyNamedArgumentReferenceProvider.java
new file mode 100644
index 000000000000..396f0b3ec7d3
--- /dev/null
+++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/extensions/GroovyNamedArgumentReferenceProvider.java
@@ -0,0 +1,19 @@
+package org.jetbrains.plugins.groovy.extensions;
+
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiReference;
+import com.intellij.util.ProcessingContext;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument;
+
+/**
+ * @author Sergey Evdokimov
+ */
+public interface GroovyNamedArgumentReferenceProvider {
+
+ PsiReference[] createRef(@NotNull PsiElement element,
+ @NotNull GrNamedArgument namedArgument,
+ @NotNull GroovyResolveResult resolveResult,
+ @NotNull ProcessingContext context);
+}
diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/synthetic/GrLightMethodBuilder.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/synthetic/GrLightMethodBuilder.java
index bba17b964b12..91f765846994 100644
--- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/synthetic/GrLightMethodBuilder.java
+++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/synthetic/GrLightMethodBuilder.java
@@ -17,6 +17,7 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.synthetic;
import com.intellij.codeInsight.completion.originInfo.OriginInfoAwareElement;
import com.intellij.navigation.ItemPresentation;
+import com.intellij.openapi.util.Key;
import com.intellij.psi.*;
import com.intellij.psi.impl.ElementPresentationUtil;
import com.intellij.psi.impl.PsiClassImplUtil;
@@ -56,6 +57,9 @@ import java.util.Map;
* @author Sergey Evdokimov
*/
public class GrLightMethodBuilder extends LightElement implements GrMethod, OriginInfoAwareElement {
+
+ public static final Key KIND_KEY = Key.create("GrLightMethodBuilder.Key");
+
protected String myName;
private PsiType myReturnType = PsiType.VOID;
private final GrLightModifierList myModifierList;
@@ -366,16 +370,23 @@ public class GrLightMethodBuilder extends LightElement implements GrMethod, Ori
public void acceptChildren(GroovyElementVisitor visitor) {
}
+ public static Object getMethodKind(@Nullable PsiElement method) {
+ if (method == null) return null;
+
+ if (method instanceof GrLightMethodBuilder) {
+ return ((GrLightMethodBuilder)method).getMethodKind();
+ }
+
+ return method.getUserData(KIND_KEY);
+ }
+
public static boolean checkKind(@Nullable PsiElement method, @NotNull Object kind) {
- return method instanceof GrLightMethodBuilder && kind.equals(((GrLightMethodBuilder)method).myMethodKind);
+ return kind.equals(getMethodKind(method));
}
public static boolean checkKind(@Nullable PsiElement method, @NotNull Object kind1, @NotNull Object kind2) {
- if (method instanceof GrLightMethodBuilder) {
- Object kind = ((GrLightMethodBuilder)method).getMethodKind();
- return kind1.equals(kind) || kind2.equals(kind);
- }
- return false;
+ Object kind = getMethodKind(method);
+ return kind1.equals(kind) || kind2.equals(kind);
}
@Override
diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/patterns/GroovyPatterns.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/patterns/GroovyPatterns.java
index 3902bc2d7872..293453a6327d 100644
--- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/patterns/GroovyPatterns.java
+++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/patterns/GroovyPatterns.java
@@ -67,7 +67,7 @@ public class GroovyPatterns extends PsiJavaPatterns {
return PsiJavaPatterns.psiMethod().with(new PatternCondition("GrLightMethodBuilder") {
@Override
public boolean accepts(@NotNull PsiMethod method, ProcessingContext context) {
- return method instanceof GrLightMethodBuilder && key.equals(((GrLightMethodBuilder)method).getMethodKind());
+ return GrLightMethodBuilder.checkKind(method, key);
}
});
}
diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/GroovyMethodArgumentReferenceContributor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/GroovyMethodArgumentReferenceContributor.java
new file mode 100644
index 000000000000..1869e5860055
--- /dev/null
+++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/GroovyMethodArgumentReferenceContributor.java
@@ -0,0 +1,159 @@
+package org.jetbrains.plugins.groovy.lang.resolve;
+
+import com.intellij.psi.*;
+import com.intellij.util.ProcessingContext;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.plugins.groovy.extensions.GroovyMethodInfo;
+import org.jetbrains.plugins.groovy.extensions.GroovyNamedArgumentReferenceProvider;
+import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
+import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap;
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument;
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrConditionalExpression;
+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.util.PsiUtil;
+
+import static org.jetbrains.plugins.groovy.lang.psi.patterns.GroovyPatterns.stringLiteral;
+
+/**
+ * @author Sergey Evdokimov
+ */
+public final class GroovyMethodArgumentReferenceContributor extends PsiReferenceContributor {
+
+ @Override
+ public void registerReferenceProviders(PsiReferenceRegistrar registrar) {
+ registrar.registerReferenceProvider(stringLiteral(), new MyProvider());
+ }
+
+ private static class MyProvider extends PsiReferenceProvider {
+ @NotNull
+ @Override
+ public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) {
+ GrExpression argument = (GrExpression)element;
+ PsiElement parent = element.getParent();
+
+ if (parent instanceof GrConditionalExpression) {
+ // support case: foo(a > b ? "aaa" : "bbb")
+
+ if (((GrConditionalExpression)parent).getCondition() == parent) return PsiReference.EMPTY_ARRAY;
+ argument = (GrConditionalExpression)parent;
+ parent = parent.getParent();
+ }
+
+ if (parent instanceof GrListOrMap) {
+ // support case: foo(["aaa", "bbb"])
+ if (!((GrListOrMap)parent).isMap()) {
+ argument = (GrListOrMap)parent;
+ parent = parent.getParent();
+ }
+ }
+
+ if (parent instanceof GrNamedArgument) {
+ return createReferencesForNamedArgument(element, (GrNamedArgument)parent, context);
+ }
+
+ if (parent instanceof GrArgumentList) {
+ GrArgumentList argumentList = (GrArgumentList)parent;
+ int index = argumentList.getExpressionArgumentIndex(argument);
+
+ PsiElement call = argumentList.getParent();
+ if (!(call instanceof GrMethodCall)) return PsiReference.EMPTY_ARRAY;
+
+ return PsiReference.EMPTY_ARRAY;
+ }
+
+ return PsiReference.EMPTY_ARRAY;
+ }
+
+ private static PsiReference[] createReferencesForNamedArgument(@NotNull PsiElement element,
+ GrNamedArgument namedArgument,
+ @NotNull ProcessingContext context) {
+ String labelName = namedArgument.getLabelName();
+ if (labelName == null) return PsiReference.EMPTY_ARRAY;
+
+ if (!GroovyMethodInfo.getAllSupportedNamedArguments().contains(labelName)) {
+ // Optimization: avoid unnecessary resolve.
+ return PsiReference.EMPTY_ARRAY;
+ }
+
+ PsiElement call = PsiUtil.getCallByNamedParameter(namedArgument);
+
+ if (!(call instanceof GrMethodCall)) return PsiReference.EMPTY_ARRAY;
+
+ GrExpression invokedExpression = ((GrMethodCall)call).getInvokedExpression();
+ if (!(invokedExpression instanceof GrReferenceExpression)) return PsiReference.EMPTY_ARRAY;
+
+ for (GroovyResolveResult result : ((GrReferenceExpression)invokedExpression).multiResolve(false)) {
+ PsiElement eMethod = result.getElement();
+ if (!(eMethod instanceof PsiMethod)) continue;
+
+ PsiMethod method = (PsiMethod)eMethod;
+
+ for (GroovyMethodInfo info : GroovyMethodInfo.getInfos(method)) {
+ Object referenceProvider = info.getNamedArgReferenceProvider(labelName);
+ if (referenceProvider != null) {
+ PsiReference[] refs;
+
+ if (referenceProvider instanceof GroovyNamedArgumentReferenceProvider) {
+ refs = ((GroovyNamedArgumentReferenceProvider)referenceProvider).createRef(element, namedArgument, result, context);
+ }
+ else {
+ refs = ((PsiReferenceProvider)referenceProvider).getReferencesByElement(element, context);
+ }
+
+ if (refs.length > 0) {
+ return refs;
+ }
+ }
+ }
+ }
+
+ return PsiReference.EMPTY_ARRAY;
+ }
+
+ //@NotNull
+ //private PsiReference[] createReferencesForArgument(@NotNull PsiElement element,
+ // int index,
+ // @NotNull GrMethodCall methodCall) {
+ // GrExpression invokedExpression = methodCall.getInvokedExpression();
+ // if (!(invokedExpression instanceof GrReferenceExpression)) return PsiReference.EMPTY_ARRAY;
+ //
+ // String methodName = ((GrReferenceExpression)invokedExpression).getReferenceName();
+ // if (methodName == null) return PsiReference.EMPTY_ARRAY;
+ //
+ // for (String key : new String[] {methodName, null}) {
+ // for (GroovyResolveResult result : ((GrReferenceExpression)invokedExpression).multiResolve(false)) {
+ // PsiElement eMethod = result.getElement();
+ // if (eMethod instanceof PsiMethod) {
+ // PsiMethod method = (PsiMethod)eMethod;
+ //
+ // //noinspection ConstantConditions
+ // if (key != null && !key.equals(method.getName())) continue;
+ //
+ // for (Pair> pair : list) {
+ // if (pair.second.value(method)) {
+ // PsiReference[] res;
+ // if (attrNameOrParameterIndex instanceof Integer) {
+ // res = pair.first.createRef(element, methodCall, (Integer)attrNameOrParameterIndex, result);
+ // }
+ // else {
+ // assert namedArgument != null;
+ // res = pair.first.createRef(element, namedArgument, result);
+ // }
+ //
+ // if (res.length > 0) {
+ // return res;
+ // }
+ // }
+ // }
+ // }
+ // }
+ // }
+ //
+ // return PsiReference.EMPTY_ARRAY;
+ //}
+ }
+
+}
diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/util/dynamicMembers/DynamicMemberUtils.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/util/dynamicMembers/DynamicMemberUtils.java
index 9536bd0a2f3c..2f8cff1e22de 100644
--- a/plugins/groovy/src/org/jetbrains/plugins/groovy/util/dynamicMembers/DynamicMemberUtils.java
+++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/util/dynamicMembers/DynamicMemberUtils.java
@@ -35,6 +35,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefini
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrAccessorMethod;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrReflectedMethod;
+import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightMethodBuilder;
import org.jetbrains.plugins.groovy.lang.psi.util.StaticChecker;
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
import org.jetbrains.plugins.groovy.lang.resolve.processors.ClassHint;
@@ -198,6 +199,14 @@ public class DynamicMemberUtils {
}
dynamicMethod.setOriginalInfo(originalInfo);
+ String kind = commentMap.get("kind");
+ if (kind == null) {
+ kind = classCommentMap.get("kind");
+ }
+ if (kind != null) {
+ dynamicMethod.putUserData(GrLightMethodBuilder.KIND_KEY, kind);
+ }
+
multiMap.putValue(null, dynamicMethod);
multiMap.putValue(method.getName(), dynamicMethod);