diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index a2c7b3152d6..206ae2c5e69 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -226,6 +226,7 @@ order="first"/> + diff --git a/idea/src/org/jetbrains/jet/plugin/JetBundle.properties b/idea/src/org/jetbrains/jet/plugin/JetBundle.properties index 3fd4c5848b8..47bb855cc04 100644 --- a/idea/src/org/jetbrains/jet/plugin/JetBundle.properties +++ b/idea/src/org/jetbrains/jet/plugin/JetBundle.properties @@ -232,4 +232,25 @@ add.function.to.type.action.type.chooser=Choose type... remove.expression = Remove ''{0}'' unwrap.expression = Unwrap ''{0}'' remove.else = Remove else in ''{0}'' -unwrap.else = Unwrap else in ''{0}'' \ No newline at end of file +unwrap.else = Unwrap else in ''{0}'' + +usageType.annotation.type = Annotation +usageType.import = Import directive +usageType.callable.reference = Callable reference +usageType.type.constraint = Type constraint +usageType.type.argument = Type argument +usageType.value.parameter.type = Parameter type +usageType.nonLocal.property.type = Class/object property type +usageType.local.variable.type = Local variable type +usageType.function.return.type = Function return types +usageType.superType = Supertype +usageType.type.definition = Type definition +usageType.is = Target type of 'is' operation +usageType.as = Target type of 'as' operation +usageType.class.object = Nested class/object +usageType.instantiation = Instantiation +usageType.function.call = Function call +usageType.extension.receiver.type = Extension receiver type +usageType.super.type.qualifier = Super type qualifier +usageType.receiver = Receiver +usageType.selector = Selector \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/plugin/findUsages/JetUsageTypeProvider.java b/idea/src/org/jetbrains/jet/plugin/findUsages/JetUsageTypeProvider.java new file mode 100644 index 00000000000..2b433894130 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/findUsages/JetUsageTypeProvider.java @@ -0,0 +1,219 @@ +/* + * Copyright 2010-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. + */ + +package org.jetbrains.jet.plugin.findUsages; + +import com.google.common.base.Predicate; +import com.intellij.psi.PsiElement; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.usages.UsageTarget; +import com.intellij.usages.impl.rules.UsageType; +import com.intellij.usages.impl.rules.UsageTypeProviderEx; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lexer.JetTokens; +import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache; + +public class JetUsageTypeProvider implements UsageTypeProviderEx { + private static final Predicate IS_ASSIGNMENT = new Predicate() { + @Override + public boolean apply(@Nullable PsiElement input) { + return input != null && JetPsiUtil.isAssignment(input); + } + }; + + @Nullable + @Override + public UsageType getUsageType(PsiElement element) { + return getUsageType(element, UsageTarget.EMPTY_ARRAY); + } + + @Nullable + @Override + public UsageType getUsageType(PsiElement element, @NotNull UsageTarget[] targets) { + if (element == null) return null; + + UsageType usageType = getCommonUsageType(element); + if (usageType != null) return usageType; + + JetSimpleNameExpression reference = PsiTreeUtil.getParentOfType(element, JetSimpleNameExpression.class, false); + if (reference == null) return null; + + BindingContext bindingContext = + AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) element.getContainingFile()).getBindingContext(); + DeclarationDescriptor descriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, reference); + + if (descriptor instanceof ClassifierDescriptor) { + return getClassUsageType(element); + } + if (descriptor instanceof VariableDescriptor) { + return getVariableUsageType(element); + } + if (descriptor instanceof FunctionDescriptor) { + return getFunctionUsageType(element, (FunctionDescriptor) descriptor); + } + + return null; + } + + private static UsageType getCommonUsageType(@NotNull PsiElement element) { + JetImportDirective importDirective = PsiTreeUtil.getParentOfType(element, JetImportDirective.class, false); + if (importDirective != null) return JetUsageTypes.IMPORT_DIRECTIVE; + + JetCallableReferenceExpression callableReference = + PsiTreeUtil.getParentOfType(element, JetCallableReferenceExpression.class, false); + if (callableReference != null && PsiTreeUtil.isAncestor(callableReference.getCallableReference(), element, false)) { + return JetUsageTypes.CALLABLE_REFERENCE; + } + + return null; + } + + @Nullable + private static UsageType getClassUsageType(@NotNull PsiElement element) { + JetTypeParameter typeParameter = PsiTreeUtil.getParentOfType(element, JetTypeParameter.class, false); + if (typeParameter != null && PsiTreeUtil.isAncestor(typeParameter.getExtendsBound(), element, false)) { + return JetUsageTypes.TYPE_CONSTRAINT; + } + + JetTypeConstraint typeConstraint = PsiTreeUtil.getParentOfType(element, JetTypeConstraint.class, false); + if (typeConstraint != null && PsiTreeUtil.isAncestor(typeConstraint.getBoundTypeReference(), element, false)) { + return JetUsageTypes.TYPE_CONSTRAINT; + } + + JetDelegationSpecifier delegationSpecifier = PsiTreeUtil.getParentOfType(element, JetDelegationSpecifier.class, false); + if (delegationSpecifier != null + && (delegationSpecifier == element || PsiTreeUtil.isAncestor(delegationSpecifier.getTypeReference(), element, false))) { + return JetUsageTypes.SUPER_TYPE; + } + + JetTypedef typedef = PsiTreeUtil.getParentOfType(element, JetTypedef.class, false); + if (typedef != null && PsiTreeUtil.isAncestor(typedef.getTypeReference(), element, false)) { + return JetUsageTypes.TYPE_DEFINITION; + } + + JetTypeProjection typeProjection = PsiTreeUtil.getParentOfType(element, JetTypeProjection.class, false); + if (typeProjection != null) return JetUsageTypes.TYPE_ARGUMENT; + + JetParameter parameter = PsiTreeUtil.getParentOfType(element, JetParameter.class, false); + if (parameter != null) { + if (PsiTreeUtil.isAncestor(parameter.getTypeReference(), element, false)) return JetUsageTypes.VALUE_PARAMETER_TYPE; + } + + JetProperty property = PsiTreeUtil.getParentOfType(element, JetProperty.class, false); + if (property != null) { + if (PsiTreeUtil.isAncestor(property.getTypeRef(), element, false)) { + return property.isLocal() ? JetUsageTypes.LOCAL_VARIABLE_TYPE : JetUsageTypes.NON_LOCAL_PROPERTY_TYPE; + } + if (PsiTreeUtil.isAncestor(property.getReceiverTypeRef(), element, false)) { + return JetUsageTypes.EXTENSION_RECEIVER_TYPE; + } + } + + JetFunction function = PsiTreeUtil.getParentOfType(element, JetFunction.class, false); + if (function != null) { + if (PsiTreeUtil.isAncestor(function.getReturnTypeRef(), element, false)) { + return JetUsageTypes.FUNCTION_RETURN_TYPE; + } + if (PsiTreeUtil.isAncestor(function.getReceiverTypeRef(), element, false)) { + return JetUsageTypes.EXTENSION_RECEIVER_TYPE; + } + } + + JetIsExpression isExpression = PsiTreeUtil.getParentOfType(element, JetIsExpression.class, false); + if (isExpression != null && PsiTreeUtil.isAncestor(isExpression.getTypeRef(), element, false)) { + return JetUsageTypes.IS; + } + + JetBinaryExpressionWithTypeRHS typeRHSExpression = + PsiTreeUtil.getParentOfType(element, JetBinaryExpressionWithTypeRHS.class, false); + if (typeRHSExpression != null && PsiTreeUtil.isAncestor(typeRHSExpression.getRight(), element, false)) { + IElementType opType = typeRHSExpression.getOperationReference().getReferencedNameElementType(); + if (opType == JetTokens.AS_KEYWORD || opType == JetTokens.AS_SAFE) return JetUsageTypes.AS; + } + + JetDotQualifiedExpression dotQualifiedExpression = PsiTreeUtil.getParentOfType(element, JetDotQualifiedExpression.class, false); + if (dotQualifiedExpression != null + && dotQualifiedExpression.getReceiverExpression() instanceof JetSimpleNameExpression + && PsiTreeUtil.isAncestor(dotQualifiedExpression.getReceiverExpression(), element, false)) { + + return JetUsageTypes.CLASS_OBJECT_ACCESS; + } + + JetSuperExpression superExpression = PsiTreeUtil.getParentOfType(element, JetSuperExpression.class, false); + if (superExpression != null && PsiTreeUtil.isAncestor(superExpression.getSuperTypeQualifier(), element, false)) { + return JetUsageTypes.SUPER_TYPE_QUALIFIER; + } + + return null; + } + + @Nullable + private static UsageType getFunctionUsageType(@NotNull PsiElement element, @NotNull FunctionDescriptor descriptor) { + if (descriptor instanceof ConstructorDescriptor) { + JetAnnotationEntry annotation = PsiTreeUtil.getParentOfType(element, JetAnnotationEntry.class, false); + if (annotation != null && PsiTreeUtil.isAncestor(annotation.getTypeReference(), element, false)) { + return JetUsageTypes.ANNOTATION_TYPE; + } + } + + JetCallExpression callExpression = PsiTreeUtil.getParentOfType(element, JetCallExpression.class, false); + if (callExpression != null + && callExpression.getCalleeExpression() instanceof JetSimpleNameExpression + && PsiTreeUtil.isAncestor(callExpression.getCalleeExpression(), element, false)) { + + return (descriptor instanceof ConstructorDescriptor) ? JetUsageTypes.INSTANTIATION : JetUsageTypes.FUNCTION_CALL; + } + + return null; + } + + @Nullable + private static UsageType getVariableUsageType(@NotNull PsiElement element) { + JetDotQualifiedExpression dotQualifiedExpression = PsiTreeUtil.getParentOfType(element, JetDotQualifiedExpression.class, false); + if (dotQualifiedExpression != null) { + if (PsiTreeUtil.isAncestor(dotQualifiedExpression.getReceiverExpression(), element, false)) { + return JetUsageTypes.RECEIVER; + } + + PsiElement parent = dotQualifiedExpression.getParent(); + if (parent instanceof JetDotQualifiedExpression + && PsiTreeUtil.isAncestor(((JetDotQualifiedExpression) parent).getReceiverExpression(), element, false)) { + return JetUsageTypes.RECEIVER; + } + + return JetUsageTypes.SELECTOR; + } + + JetBinaryExpression binaryExpression = + (JetBinaryExpression) JetPsiUtil.getParentByTypeAndPredicate(element, JetBinaryExpression.class, IS_ASSIGNMENT, false); + if (binaryExpression != null && PsiTreeUtil.isAncestor(binaryExpression.getLeft(), element, false)) { + return UsageType.WRITE; + } + + JetSimpleNameExpression simpleNameExpression = PsiTreeUtil.getParentOfType(element, JetSimpleNameExpression.class, false); + if (simpleNameExpression != null) { + return UsageType.READ; + } + + return null; + } +} + diff --git a/idea/src/org/jetbrains/jet/plugin/findUsages/JetUsageTypes.java b/idea/src/org/jetbrains/jet/plugin/findUsages/JetUsageTypes.java new file mode 100644 index 00000000000..4699d618bf8 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/findUsages/JetUsageTypes.java @@ -0,0 +1,53 @@ +/* + * Copyright 2010-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. + */ + +package org.jetbrains.jet.plugin.findUsages; + +import com.intellij.usages.impl.rules.UsageType; +import org.jetbrains.jet.plugin.JetBundle; + +public class JetUsageTypes { + private JetUsageTypes() { + } + + // Type-related usages + public static final UsageType ANNOTATION_TYPE = new UsageType(JetBundle.message("usageType.annotation.type")); + public static final UsageType TYPE_CONSTRAINT = new UsageType(JetBundle.message("usageType.type.constraint")); + public static final UsageType TYPE_ARGUMENT = new UsageType(JetBundle.message("usageType.type.argument")); + public static final UsageType VALUE_PARAMETER_TYPE = new UsageType(JetBundle.message("usageType.value.parameter.type")); + public static final UsageType NON_LOCAL_PROPERTY_TYPE = new UsageType(JetBundle.message("usageType.nonLocal.property.type")); + public static final UsageType LOCAL_VARIABLE_TYPE = new UsageType(JetBundle.message("usageType.local.variable.type")); + public static final UsageType FUNCTION_RETURN_TYPE = new UsageType(JetBundle.message("usageType.function.return.type")); + public static final UsageType SUPER_TYPE = new UsageType(JetBundle.message("usageType.superType")); + public static final UsageType TYPE_DEFINITION = new UsageType(JetBundle.message("usageType.type.definition")); + public static final UsageType AS = new UsageType(JetBundle.message("usageType.as")); + public static final UsageType IS = new UsageType(JetBundle.message("usageType.is")); + public static final UsageType CLASS_OBJECT_ACCESS = new UsageType(JetBundle.message("usageType.class.object")); + public static final UsageType EXTENSION_RECEIVER_TYPE = new UsageType(JetBundle.message("usageType.extension.receiver.type")); + public static final UsageType SUPER_TYPE_QUALIFIER = new UsageType(JetBundle.message("usageType.super.type.qualifier")); + + // Function-related usages + public static final UsageType INSTANTIATION = new UsageType(JetBundle.message("usageType.instantiation")); + public static final UsageType FUNCTION_CALL = new UsageType(JetBundle.message("usageType.function.call")); + + // Value-related usages + public static final UsageType RECEIVER = new UsageType(JetBundle.message("usageType.receiver")); + public static final UsageType SELECTOR = new UsageType(JetBundle.message("usageType.selector")); + + // Miscellaneous usages + public static final UsageType IMPORT_DIRECTIVE = new UsageType(JetBundle.message("usageType.import")); + public static final UsageType CALLABLE_REFERENCE = new UsageType(JetBundle.message("usageType.callable.reference")); +} diff --git a/idea/src/org/jetbrains/jet/plugin/search/KotlinReferencesSearcher.java b/idea/src/org/jetbrains/jet/plugin/search/KotlinReferencesSearcher.java index a1d59dd58f8..dcd74e77463 100644 --- a/idea/src/org/jetbrains/jet/plugin/search/KotlinReferencesSearcher.java +++ b/idea/src/org/jetbrains/jet/plugin/search/KotlinReferencesSearcher.java @@ -27,26 +27,33 @@ import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.util.Processor; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.asJava.LightClassUtil; -import org.jetbrains.jet.lang.psi.JetClass; -import org.jetbrains.jet.lang.psi.JetNamedFunction; +import org.jetbrains.jet.lang.psi.*; public class KotlinReferencesSearcher extends QueryExecutorBase { + public static void processJetClassOrObject( + final @NotNull JetClassOrObject element, @NotNull ReferencesSearch.SearchParameters queryParameters + ) { + String className = element.getName(); + if (className != null) { + PsiClass lightClass = ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public PsiClass compute() { + return LightClassUtil.getPsiClass(element); + } + }); + if (lightClass != null) { + queryParameters.getOptimizer().searchWord(className, queryParameters.getEffectiveSearchScope(), true, lightClass); + } + } + } + + + @Override public void processQuery(@NotNull ReferencesSearch.SearchParameters queryParameters, @NotNull Processor consumer) { - final PsiElement element = queryParameters.getElementToSearch(); + PsiElement element = queryParameters.getElementToSearch(); if (element instanceof JetClass) { - String className = ((JetClass) element).getName(); - if (className != null) { - PsiClass lightClass = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public PsiClass compute() { - return LightClassUtil.getPsiClass((JetClass) element); - } - }); - if (lightClass != null) { - queryParameters.getOptimizer().searchWord(className, queryParameters.getEffectiveSearchScope(), true, lightClass); - } - } + processJetClassOrObject((JetClass) element, queryParameters); } else if (element instanceof JetNamedFunction) { final JetNamedFunction function = (JetNamedFunction) element; @@ -58,10 +65,33 @@ public class KotlinReferencesSearcher extends QueryExecutorBase() { + @Override + public LightClassUtil.PropertyAccessorsPsiMethods compute() { + return LightClassUtil.getLightClassPropertyMethods(property); + } + }); + + searchMethod(queryParameters, propertyMethods.getGetter()); + searchMethod(queryParameters, propertyMethods.getSetter()); + } + else if (element instanceof JetObjectDeclarationName) { + PsiElement parent = element.getParent(); + if (parent instanceof JetObjectDeclaration) { + processJetClassOrObject((JetObjectDeclaration)parent, queryParameters); } } } + + private static void searchMethod(ReferencesSearch.SearchParameters queryParameters, PsiMethod method) { + if (method != null) { + queryParameters.getOptimizer().searchWord(method.getName(), queryParameters.getEffectiveSearchScope(), true, method); + } + } } diff --git a/idea/testData/findUsages/findClassUsages/Client.java b/idea/testData/findUsages/findClassUsages/javaUsages/Client.java similarity index 100% rename from idea/testData/findUsages/findClassUsages/Client.java rename to idea/testData/findUsages/findClassUsages/javaUsages/Client.java diff --git a/idea/testData/findUsages/findClassUsages/Server.kt b/idea/testData/findUsages/findClassUsages/javaUsages/Server.kt similarity index 100% rename from idea/testData/findUsages/findClassUsages/Server.kt rename to idea/testData/findUsages/findClassUsages/javaUsages/Server.kt diff --git a/idea/testData/findUsages/findClassUsages/kotlinUsages1/Client.kt b/idea/testData/findUsages/findClassUsages/kotlinUsages1/Client.kt new file mode 100644 index 00000000000..b24ac3812cf --- /dev/null +++ b/idea/testData/findUsages/findClassUsages/kotlinUsages1/Client.kt @@ -0,0 +1,34 @@ +package client + +import server.Server + +class Client: Server { + var nextServer: Server? = null + val name = Server.NAME + + fun foo(s: Server) { + val server: Server = s + println("Server: $server") + } + + fun getNextServer(): Server? { + return nextServer + } + + override fun work() { + super.work() + println("Client") + } +} + +fun Client.bar(s: Server) { + foo(s) +} + +fun Client.hasNextServer(): Boolean { + return getNextServer() != null +} + +fun Any.asServer(): Server? { + return if (this is Server) this as Server else null +} \ No newline at end of file diff --git a/idea/testData/findUsages/findClassUsages/kotlinUsages1/Server.kt b/idea/testData/findUsages/findClassUsages/kotlinUsages1/Server.kt new file mode 100644 index 00000000000..e8f74cc9821 --- /dev/null +++ b/idea/testData/findUsages/findClassUsages/kotlinUsages1/Server.kt @@ -0,0 +1,11 @@ +package server + +open class Server { + class object { + val NAME = "Server" + } + + open fun work() { + println("Server") + } +} \ No newline at end of file diff --git a/idea/testData/findUsages/findClassUsages/kotlinUsages2/Client.kt b/idea/testData/findUsages/findClassUsages/kotlinUsages2/Client.kt new file mode 100644 index 00000000000..8511a3a9e92 --- /dev/null +++ b/idea/testData/findUsages/findClassUsages/kotlinUsages2/Client.kt @@ -0,0 +1,8 @@ +package client + +import server.X + +X("") +class Client { + +} \ No newline at end of file diff --git a/idea/testData/findUsages/findClassUsages/kotlinUsages2/Server.kt b/idea/testData/findUsages/findClassUsages/kotlinUsages2/Server.kt new file mode 100644 index 00000000000..92281e3ae02 --- /dev/null +++ b/idea/testData/findUsages/findClassUsages/kotlinUsages2/Server.kt @@ -0,0 +1,3 @@ +package server + +annotation class X(val x: String) \ No newline at end of file diff --git a/idea/testData/findUsages/findClassUsages/kotlinUsages3/Client.kt b/idea/testData/findUsages/findClassUsages/kotlinUsages3/Client.kt new file mode 100644 index 00000000000..92d7b496461 --- /dev/null +++ b/idea/testData/findUsages/findClassUsages/kotlinUsages3/Client.kt @@ -0,0 +1,9 @@ +package client + +import server.Data + +class Client where X: Data { + +} + +val c = Client() \ No newline at end of file diff --git a/idea/testData/findUsages/findClassUsages/kotlinUsages3/Server.kt b/idea/testData/findUsages/findClassUsages/kotlinUsages3/Server.kt new file mode 100644 index 00000000000..2b4068b1e88 --- /dev/null +++ b/idea/testData/findUsages/findClassUsages/kotlinUsages3/Server.kt @@ -0,0 +1,3 @@ +package server + +data class Data \ No newline at end of file diff --git a/idea/testData/findUsages/findMethodUsages/Client.java b/idea/testData/findUsages/findMethodUsages/javaUsages/Client.java similarity index 100% rename from idea/testData/findUsages/findMethodUsages/Client.java rename to idea/testData/findUsages/findMethodUsages/javaUsages/Client.java diff --git a/idea/testData/findUsages/findMethodUsages/Server.kt b/idea/testData/findUsages/findMethodUsages/javaUsages/Server.kt similarity index 100% rename from idea/testData/findUsages/findMethodUsages/Server.kt rename to idea/testData/findUsages/findMethodUsages/javaUsages/Server.kt diff --git a/idea/testData/findUsages/findMethodUsages/kotlinUsages/Client.kt b/idea/testData/findUsages/findMethodUsages/kotlinUsages/Client.kt new file mode 100644 index 00000000000..9bd981a0c77 --- /dev/null +++ b/idea/testData/findUsages/findMethodUsages/kotlinUsages/Client.kt @@ -0,0 +1,12 @@ +package client + +import server.processRequest + +class Client { + val methodRef = ::processRequest() + + fun doProcessRequest() { + println("Process...") + processRequest() + } +} \ No newline at end of file diff --git a/idea/testData/findUsages/findMethodUsages/kotlinUsages/Server.kt b/idea/testData/findUsages/findMethodUsages/kotlinUsages/Server.kt new file mode 100644 index 00000000000..4c303b09795 --- /dev/null +++ b/idea/testData/findUsages/findMethodUsages/kotlinUsages/Server.kt @@ -0,0 +1,4 @@ +package server; + +fun processRequest() = "foo" + diff --git a/idea/testData/findUsages/findObjectUsages/javaUsages/Client.java b/idea/testData/findUsages/findObjectUsages/javaUsages/Client.java new file mode 100644 index 00000000000..b896d9becc3 --- /dev/null +++ b/idea/testData/findUsages/findObjectUsages/javaUsages/Client.java @@ -0,0 +1,9 @@ +package client; + +import server.O + +class Client { + fun fooBar() { + println("foo = " + O.instance$.getFoo()) + } +} \ No newline at end of file diff --git a/idea/testData/findUsages/findObjectUsages/javaUsages/Server.kt b/idea/testData/findUsages/findObjectUsages/javaUsages/Server.kt new file mode 100644 index 00000000000..52c87b49e49 --- /dev/null +++ b/idea/testData/findUsages/findObjectUsages/javaUsages/Server.kt @@ -0,0 +1,6 @@ +package server; + +object O { + var foo: String = "foo" +} + diff --git a/idea/testData/findUsages/findObjectUsages/kotlinUsages/Client.kt b/idea/testData/findUsages/findObjectUsages/kotlinUsages/Client.kt new file mode 100644 index 00000000000..7b0126d1a91 --- /dev/null +++ b/idea/testData/findUsages/findObjectUsages/kotlinUsages/Client.kt @@ -0,0 +1,11 @@ +package client + +import server.O + +class Client { + fun fooBar() { + println("foo = ${O.foo}") + val obj = O + println("length: ${obj.foo.length()}") + } +} \ No newline at end of file diff --git a/idea/testData/findUsages/findObjectUsages/kotlinUsages/Server.kt b/idea/testData/findUsages/findObjectUsages/kotlinUsages/Server.kt new file mode 100644 index 00000000000..52c87b49e49 --- /dev/null +++ b/idea/testData/findUsages/findObjectUsages/kotlinUsages/Server.kt @@ -0,0 +1,6 @@ +package server; + +object O { + var foo: String = "foo" +} + diff --git a/idea/testData/findUsages/findPropertyUsages/javaUsages/Client.java b/idea/testData/findUsages/findPropertyUsages/javaUsages/Client.java new file mode 100644 index 00000000000..b896d9becc3 --- /dev/null +++ b/idea/testData/findUsages/findPropertyUsages/javaUsages/Client.java @@ -0,0 +1,9 @@ +package client; + +import server.O + +class Client { + fun fooBar() { + println("foo = " + O.instance$.getFoo()) + } +} \ No newline at end of file diff --git a/idea/testData/findUsages/findPropertyUsages/javaUsages/Server.kt b/idea/testData/findUsages/findPropertyUsages/javaUsages/Server.kt new file mode 100644 index 00000000000..388e800f3d2 --- /dev/null +++ b/idea/testData/findUsages/findPropertyUsages/javaUsages/Server.kt @@ -0,0 +1,6 @@ +package server; + +object O { + var foo: String = "foo" +} + diff --git a/idea/testData/findUsages/findPropertyUsages/kotlinUsages/Client.kt b/idea/testData/findUsages/findPropertyUsages/kotlinUsages/Client.kt new file mode 100644 index 00000000000..4d9a435b595 --- /dev/null +++ b/idea/testData/findUsages/findPropertyUsages/kotlinUsages/Client.kt @@ -0,0 +1,10 @@ +package client + +import server.foo + +class Client { + fun fooBar() { + println("foo = ${server.foo}") + println("length: ${server.foo.length()}") + } +} \ No newline at end of file diff --git a/idea/testData/findUsages/findPropertyUsages/kotlinUsages/Server.kt b/idea/testData/findUsages/findPropertyUsages/kotlinUsages/Server.kt new file mode 100644 index 00000000000..179159c259c --- /dev/null +++ b/idea/testData/findUsages/findPropertyUsages/kotlinUsages/Server.kt @@ -0,0 +1,4 @@ +package server; + +var foo: String = "foo" + diff --git a/idea/tests/org/jetbrains/jet/findUsages/JetFindUsagesTest.java b/idea/tests/org/jetbrains/jet/findUsages/JetFindUsagesTest.java index 5473b55c665..33e8c702c07 100644 --- a/idea/tests/org/jetbrains/jet/findUsages/JetFindUsagesTest.java +++ b/idea/tests/org/jetbrains/jet/findUsages/JetFindUsagesTest.java @@ -22,18 +22,24 @@ import com.google.common.base.Predicates; import com.google.common.collect.Collections2; import com.google.common.collect.Lists; import com.google.common.collect.Ordering; +import com.intellij.openapi.extensions.Extensions; +import com.intellij.psi.PsiComment; import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.testFramework.LightProjectDescriptor; import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; import com.intellij.usageView.UsageInfo; import com.intellij.usages.UsageInfo2UsageAdapter; +import com.intellij.usages.impl.rules.UsageType; +import com.intellij.usages.impl.rules.UsageTypeProvider; import com.intellij.usages.rules.UsageFilteringRule; import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.psi.JetClass; import org.jetbrains.jet.lang.psi.JetFunction; +import org.jetbrains.jet.lang.psi.JetObjectDeclarationName; +import org.jetbrains.jet.lang.psi.JetProperty; import org.jetbrains.jet.plugin.JetLightProjectDescriptor; import org.jetbrains.jet.plugin.PluginTestCaseBase; import org.jetbrains.jet.plugin.findUsages.JetImportFilteringRule; @@ -55,25 +61,119 @@ public class JetFindUsagesTest extends LightCodeInsightFixtureTestCase { myFixture.setTestDataPath(PluginTestCaseBase.getTestDataPathBase() + "/findUsages"); } - public void testFindClassUsages() { + public void testFindClassJavaUsages() { doTestWithoutFiltering( - Lists.newArrayList("findClassUsages/Server.kt", "findClassUsages/Client.java"), + Lists.newArrayList("findClassUsages/javaUsages/Server.kt", "findClassUsages/javaUsages/Client.java"), JetClass.class, - Lists.newArrayList("(4: 13) private Server myServer;")); + Lists.newArrayList("Field declaration (4: 13) private Server myServer;")); + } + + public void testFindClassKotlinUsages1() { + doTestWithoutFiltering( + Lists.newArrayList("findClassUsages/kotlinUsages1/Server.kt", "findClassUsages/kotlinUsages1/Client.kt"), + JetClass.class, + Lists.newArrayList( + "Class/object property type (6: 21) var nextServer: Server? = null", + "Function return types (14: 26) fun getNextServer(): Server? {", + "Function return types (32: 21) fun Any.asServer(): Server? {", + "Import directive (3: 15) import server.Server", + "Local variable type (10: 21) val server: Server = s", + "Nested class/object (7: 16) val name = Server.NAME", + "Parameter type (24: 19) fun Client.bar(s: Server) {", + "Parameter type (9: 16) fun foo(s: Server) {", + "Super type qualifier (19: 15) super.work()", + "Supertype (5: 15) class Client: Server {", + "Target type of 'as' operation (33: 40) return if (this is Server) this as Server else null", + "Target type of 'is' operation (33: 24) return if (this is Server) this as Server else null" + )); + } + + public void testFindClassKotlinUsages2() { + doTestWithoutFiltering( + Lists.newArrayList("findClassUsages/kotlinUsages2/Server.kt", "findClassUsages/kotlinUsages2/Client.kt"), + JetClass.class, + Lists.newArrayList( + "Annotation (5: 1) X(\"\")", + "Import directive (3: 15) import server.X" + )); + } + + public void testFindClassKotlinUsages3() { + doTestWithoutFiltering( + Lists.newArrayList("findClassUsages/kotlinUsages3/Server.kt", "findClassUsages/kotlinUsages3/Client.kt"), + JetClass.class, + Lists.newArrayList( + "Import directive (3: 15) import server.Data", + "Type argument (9: 16) val c = Client()", + "Type constraint (5: 17) class Client where X: Data {", + "Type constraint (5: 35) class Client where X: Data {" + )); } public void testFindUsagesUnresolvedAnnotation() { doTestWithoutFiltering( Lists.newArrayList("unresolvedAnnotation/Server.kt", "unresolvedAnnotation/Client.java"), JetClass.class, - Lists.newArrayList("(1: 29) public class Client extends Foo {}")); + Lists.newArrayList("Usage in extends/implements clause (1: 29) public class Client extends Foo {}")); } - public void testFindMethodUsages() { + public void testFindMethodJavaUsages() { doTestWithoutFiltering( - Lists.newArrayList("findMethodUsages/Server.kt", "findMethodUsages/Client.java"), + Lists.newArrayList("findMethodUsages/javaUsages/Server.kt", "findMethodUsages/javaUsages/Client.java"), JetFunction.class, - Lists.newArrayList("(6: 16) server.processRequest();")); + Lists.newArrayList("Unclassified usage (6: 16) server.processRequest();")); + } + + public void testFindMethodKotlinUsages() { + doTestWithoutFiltering( + Lists.newArrayList("findMethodUsages/kotlinUsages/Server.kt", "findMethodUsages/kotlinUsages/Client.kt"), + JetFunction.class, + Lists.newArrayList( + "Callable reference (6: 23) val methodRef = ::processRequest()", + "Function call (10: 9) processRequest()", + "Import directive (3: 15) import server.processRequest" + )); + } + + public void testFindPropertyJavaUsages() { + doTestWithoutFiltering( + Lists.newArrayList("findPropertyUsages/javaUsages/Server.kt", "findPropertyUsages/javaUsages/Client.java"), + JetProperty.class, + Lists.newArrayList( + "Unclassified usage (7: 40) println(\"foo = \" + O.instance$.getFoo())" + )); + } + + public void testFindPropertyKotlinUsages() { + doTestWithoutFiltering( + Lists.newArrayList("findPropertyUsages/kotlinUsages/Server.kt", "findPropertyUsages/kotlinUsages/Client.kt"), + JetProperty.class, + Lists.newArrayList( + "Import directive (3: 15) import server.foo", + "Receiver (8: 35) println(\"length: ${server.foo.length()}\")", + "Selector (7: 33) println(\"foo = ${server.foo}\")" + )); + } + + public void testFindObjectJavaUsages() { + doTestWithoutFiltering( + Lists.newArrayList("findObjectUsages/javaUsages/Server.kt", "findObjectUsages/javaUsages/Client.java"), + JetObjectDeclarationName.class, + Lists.newArrayList( + "Class static member access (7: 28) println(\"foo = \" + O.instance$.getFoo())", + "Usage in import (3: 15) import server.O" + )); + } + + public void testFindObjectKotlinUsages() { + doTestWithoutFiltering( + Lists.newArrayList("findObjectUsages/kotlinUsages/Server.kt", "findObjectUsages/kotlinUsages/Client.kt"), + JetObjectDeclarationName.class, + Lists.newArrayList( + "Import directive (3: 15) import server.O", + "Receiver (7: 26) println(\"foo = ${O.foo}\")", + "Value read (8: 19) val obj = O" + )); } public void testFindWithFilteringImports() { @@ -81,18 +181,22 @@ public class JetFindUsagesTest extends LightCodeInsightFixtureTestCase { JetClass.class, 2, Lists.newArrayList(new JetImportFilteringRule()), - Lists.newArrayList("(5: 9) val a = Test()")); + Lists.newArrayList("Instantiation (5: 9) val a = Test()")); } - private void doTestWithoutFiltering(Collection files, Class caretElementClass, Collection expectedUsages) { + private void doTestWithoutFiltering( + Collection files, + Class caretElementClass, + Collection expectedUsages + ) { doTest(files, caretElementClass, expectedUsages.size(), Collections.emptyList(), expectedUsages); } - private void doTest( + private void doTest( Collection files, Class caretElementClass, int numberOfNotFilteredUsages, - final Collection filters, + Collection filters, Collection expectedUsages ) { myFixture.configureByFiles(ArrayUtil.toObjectArray(files, String.class)); @@ -103,34 +207,60 @@ public class JetFindUsagesTest extends LightCodeInsightFixtureTestCase { assertEquals(numberOfNotFilteredUsages, usageInfos.size()); - Collection filteredUsages = - Collections2.filter( - Collections2.transform(usageInfos, new Function() { - @Override - public UsageInfo2UsageAdapter apply(@Nullable UsageInfo usageInfo) { - assert (usageInfo != null); - - UsageInfo2UsageAdapter usageAdapter = new UsageInfo2UsageAdapter(usageInfo); - for (UsageFilteringRule filter : filters) { - if (!filter.isVisible(usageAdapter)) { - return null; - } - } - - return usageAdapter; - } - }), - Predicates.notNull()); + Collection filteredUsages = getUsageAdapters(filters, usageInfos); Function convertToString = new Function() { @Override public String apply(@Nullable UsageInfo2UsageAdapter usageAdapter) { assert usageAdapter != null; - return Joiner.on("").join(Arrays.asList(usageAdapter.getPresentation().getText())); + return getUsageType(usageAdapter.getElement()) + + " " + Joiner.on("").join(Arrays.asList(usageAdapter.getPresentation().getText())); } }; Collection finalUsages = Ordering.natural().sortedCopy(Collections2.transform(filteredUsages, convertToString)); assertOrderedEquals(finalUsages, Ordering.natural().sortedCopy(expectedUsages)); } + + private static Collection getUsageAdapters( + final Collection filters, + Collection usageInfos + ) { + return Collections2.filter( + Collections2.transform(usageInfos, new Function() { + @Override + public UsageInfo2UsageAdapter apply(@Nullable UsageInfo usageInfo) { + assert (usageInfo != null); + + UsageInfo2UsageAdapter usageAdapter = new UsageInfo2UsageAdapter(usageInfo); + for (UsageFilteringRule filter : filters) { + if (!filter.isVisible(usageAdapter)) { + return null; + } + } + + return usageAdapter; + } + }), + Predicates.notNull()); + } + + @Nullable + private static UsageType getUsageType(PsiElement element) { + if (element == null) return null; + + if (PsiTreeUtil.getParentOfType(element, PsiComment.class, false) != null) { + return UsageType.COMMENT_USAGE; + } + + UsageTypeProvider[] providers = Extensions.getExtensions(UsageTypeProvider.EP_NAME); + for (UsageTypeProvider provider : providers) { + UsageType usageType = provider.getUsageType(element); + if (usageType != null) { + return usageType; + } + } + + return UsageType.UNCLASSIFIED; + } }