Implement Kotlin usage classification
This commit is contained in:
@@ -226,6 +226,7 @@
|
||||
order="first"/>
|
||||
<elementDescriptionProvider implementation="org.jetbrains.jet.plugin.findUsages.JetElementDescriptionProvider"/>
|
||||
<findUsagesHandlerFactory implementation="org.jetbrains.jet.plugin.findUsages.KotlinFindUsagesHandlerFactory"/>
|
||||
<usageTypeProvider implementation="org.jetbrains.jet.plugin.findUsages.JetUsageTypeProvider"/>
|
||||
<debugger.positionManagerFactory implementation="org.jetbrains.jet.plugin.debugger.JetPositionManagerFactory"/>
|
||||
<codeInsight.implementMethod language="jet" implementationClass="org.jetbrains.jet.plugin.codeInsight.ImplementMethodsHandler"/>
|
||||
<codeInsight.overrideMethod language="jet" implementationClass="org.jetbrains.jet.plugin.codeInsight.OverrideMethodsHandler"/>
|
||||
|
||||
@@ -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}''
|
||||
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
|
||||
@@ -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<PsiElement> IS_ASSIGNMENT = new Predicate<PsiElement>() {
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
@@ -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<PsiReference, ReferencesSearch.SearchParameters> {
|
||||
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<PsiClass>() {
|
||||
@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<PsiReference> 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<PsiClass>() {
|
||||
@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<PsiReference, Re
|
||||
return LightClassUtil.getLightClassMethod(function);
|
||||
}
|
||||
});
|
||||
if (method != null) {
|
||||
queryParameters.getOptimizer().searchWord(name, queryParameters.getEffectiveSearchScope(), true, method);
|
||||
}
|
||||
searchMethod(queryParameters, method);
|
||||
}
|
||||
}
|
||||
else if (element instanceof JetProperty) {
|
||||
final JetProperty property = (JetProperty) element;
|
||||
LightClassUtil.PropertyAccessorsPsiMethods propertyMethods =
|
||||
ApplicationManager.getApplication().runReadAction(new Computable<LightClassUtil.PropertyAccessorsPsiMethods>() {
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Server>.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
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package server
|
||||
|
||||
open class <caret>Server {
|
||||
class object {
|
||||
val NAME = "Server"
|
||||
}
|
||||
|
||||
open fun work() {
|
||||
println("Server")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package client
|
||||
|
||||
import server.X
|
||||
|
||||
X("")
|
||||
class Client {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package server
|
||||
|
||||
annotation class <caret>X(val x: String)
|
||||
@@ -0,0 +1,9 @@
|
||||
package client
|
||||
|
||||
import server.Data
|
||||
|
||||
class Client<T: Data, X> where X: Data {
|
||||
|
||||
}
|
||||
|
||||
val c = Client<Data>()
|
||||
@@ -0,0 +1,3 @@
|
||||
package server
|
||||
|
||||
data class <caret>Data
|
||||
@@ -0,0 +1,12 @@
|
||||
package client
|
||||
|
||||
import server.processRequest
|
||||
|
||||
class Client {
|
||||
val methodRef = ::processRequest()
|
||||
|
||||
fun doProcessRequest() {
|
||||
println("Process...")
|
||||
processRequest()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package server;
|
||||
|
||||
fun <caret>processRequest() = "foo"
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package client;
|
||||
|
||||
import server.O
|
||||
|
||||
class Client {
|
||||
fun fooBar() {
|
||||
println("foo = " + O.instance$.getFoo())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package server;
|
||||
|
||||
object <caret>O {
|
||||
var foo: String = "foo"
|
||||
}
|
||||
|
||||
@@ -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()}")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package server;
|
||||
|
||||
object <caret>O {
|
||||
var foo: String = "foo"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package client;
|
||||
|
||||
import server.O
|
||||
|
||||
class Client {
|
||||
fun fooBar() {
|
||||
println("foo = " + O.instance$.getFoo())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package server;
|
||||
|
||||
object O {
|
||||
var <caret>foo: String = "foo"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package client
|
||||
|
||||
import server.foo
|
||||
|
||||
class Client {
|
||||
fun fooBar() {
|
||||
println("foo = ${server.foo}")
|
||||
println("length: ${server.foo.length()}")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package server;
|
||||
|
||||
var <caret>foo: String = "foo"
|
||||
|
||||
@@ -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<Server>.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<Data>()",
|
||||
"Type constraint (5: 17) class Client<T: Data, X> where X: Data {",
|
||||
"Type constraint (5: 35) class Client<T: Data, X> 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 <T extends PsiElement> void doTestWithoutFiltering(Collection<String> files, Class<T> caretElementClass, Collection<String> expectedUsages) {
|
||||
private <T extends PsiElement> void doTestWithoutFiltering(
|
||||
Collection<String> files,
|
||||
Class<T> caretElementClass,
|
||||
Collection<String> expectedUsages
|
||||
) {
|
||||
doTest(files, caretElementClass, expectedUsages.size(), Collections.<UsageFilteringRule>emptyList(), expectedUsages);
|
||||
}
|
||||
|
||||
private <T extends PsiElement> void doTest(
|
||||
private <T extends PsiElement> void doTest(
|
||||
Collection<String> files,
|
||||
Class<T> caretElementClass,
|
||||
int numberOfNotFilteredUsages,
|
||||
final Collection<? extends UsageFilteringRule> filters,
|
||||
Collection<? extends UsageFilteringRule> filters,
|
||||
Collection<String> expectedUsages
|
||||
) {
|
||||
myFixture.configureByFiles(ArrayUtil.toObjectArray(files, String.class));
|
||||
@@ -103,34 +207,60 @@ public class JetFindUsagesTest extends LightCodeInsightFixtureTestCase {
|
||||
|
||||
assertEquals(numberOfNotFilteredUsages, usageInfos.size());
|
||||
|
||||
Collection<UsageInfo2UsageAdapter> filteredUsages =
|
||||
Collections2.filter(
|
||||
Collections2.transform(usageInfos, new Function<UsageInfo, UsageInfo2UsageAdapter>() {
|
||||
@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<UsageInfo2UsageAdapter> filteredUsages = getUsageAdapters(filters, usageInfos);
|
||||
|
||||
Function<UsageInfo2UsageAdapter, String> convertToString = new Function<UsageInfo2UsageAdapter, String>() {
|
||||
@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<String> finalUsages = Ordering.natural().sortedCopy(Collections2.transform(filteredUsages, convertToString));
|
||||
assertOrderedEquals(finalUsages, Ordering.natural().sortedCopy(expectedUsages));
|
||||
}
|
||||
|
||||
private static Collection<UsageInfo2UsageAdapter> getUsageAdapters(
|
||||
final Collection<? extends UsageFilteringRule> filters,
|
||||
Collection<UsageInfo> usageInfos
|
||||
) {
|
||||
return Collections2.filter(
|
||||
Collections2.transform(usageInfos, new Function<UsageInfo, UsageInfo2UsageAdapter>() {
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user