Add separate search of constructor usages

This commit is contained in:
Alexey Sedunov
2013-08-13 16:10:48 +04:00
parent 78189e883d
commit b2d1f8d323
33 changed files with 377 additions and 34 deletions
@@ -4,12 +4,13 @@ import com.intellij.find.FindBundle;
import com.intellij.find.findUsages.FindClassUsagesDialog;
import com.intellij.find.findUsages.FindUsagesHandler;
import com.intellij.find.findUsages.FindUsagesOptions;
import com.intellij.find.findUsages.JavaClassFindUsagesOptions;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiClass;
import com.intellij.ui.SimpleColoredComponent;
import com.intellij.ui.StateRestoringCheckBox;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.asJava.KotlinLightClassForExplicitDeclaration;
import org.jetbrains.jet.lang.psi.JetClass;
import org.jetbrains.jet.plugin.JetBundle;
import org.jetbrains.jet.plugin.findUsages.options.KotlinClassFindUsagesOptions;
import org.jetbrains.jet.plugin.refactoring.JetRefactoringUtil;
@@ -17,6 +18,8 @@ import org.jetbrains.jet.plugin.refactoring.JetRefactoringUtil;
import javax.swing.*;
public class KotlinFindClassUsagesDialog extends FindClassUsagesDialog {
private StateRestoringCheckBox constructorUsages;
public KotlinFindClassUsagesDialog(
PsiClass klass,
Project project,
@@ -60,6 +63,13 @@ public class KotlinFindClassUsagesDialog extends FindClassUsagesDialog {
JetBundle.message("find.what.derived.classes.checkbox")
);
constructorUsages = addCheckboxToPanel(
JetBundle.message("find.what.constructor.usages.checkbox"),
getFindUsagesOptions().searchConstructorUsages,
findWhatPanel,
true
);
return findWhatPanel;
}
@@ -75,4 +85,20 @@ public class KotlinFindClassUsagesDialog extends FindClassUsagesDialog {
coloredComponent.append(JetRefactoringUtil.formatClass(((KotlinLightClassForExplicitDeclaration) klass).getJetClassOrObject()));
}
}
@Override
protected void update() {
super.update();
if (!isOKActionEnabled() && constructorUsages.isSelected()) {
setOKActionEnabled(true);
}
}
@Override
public void calcFindUsagesOptions(JavaClassFindUsagesOptions options) {
assert options instanceof KotlinClassFindUsagesOptions;
super.calcFindUsagesOptions(options);
((KotlinClassFindUsagesOptions) options).searchConstructorUsages = constructorUsages.isSelected();
}
}
@@ -19,14 +19,26 @@ package org.jetbrains.jet.plugin.findUsages.handlers;
import com.intellij.find.findUsages.AbstractFindUsagesDialog;
import com.intellij.find.findUsages.FindUsagesOptions;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.asJava.LightClassUtil;
import org.jetbrains.jet.lang.psi.JetClass;
import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
import org.jetbrains.jet.plugin.findUsages.KotlinFindUsagesHandlerFactory;
import org.jetbrains.jet.plugin.findUsages.dialogs.KotlinFindClassUsagesDialog;
import org.jetbrains.jet.plugin.findUsages.options.KotlinClassFindUsagesOptions;
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache;
import java.util.Collection;
public class KotlinFindClassUsagesHandler extends KotlinFindUsagesHandler<JetClass> {
public KotlinFindClassUsagesHandler(@NotNull JetClass jetClass, @NotNull KotlinFindUsagesHandlerFactory factory) {
@@ -45,6 +57,72 @@ public class KotlinFindClassUsagesHandler extends KotlinFindUsagesHandler<JetCla
return super.getFindUsagesDialog(isSingleFile, toShowInNewTab, mustOpenInNewTab);
}
@Override
protected boolean searchReferences(
@NotNull PsiElement element, @NotNull Processor<UsageInfo> processor, @NotNull FindUsagesOptions options
) {
KotlinClassFindUsagesOptions kotlinOptions = (KotlinClassFindUsagesOptions)options;
JetClass jetClass = (JetClass) element;
if (kotlinOptions.isUsages || kotlinOptions.searchConstructorUsages) {
Collection<PsiReference> references = ReferencesSearch.search(
new ReferencesSearch.SearchParameters(jetClass, kotlinOptions.searchScope, false)
).findAll();
for (PsiReference ref : references) {
boolean constructorUsage = isConstructorUsage(ref.getElement(), jetClass);
if ((constructorUsage && !kotlinOptions.searchConstructorUsages)
|| (!constructorUsage && !kotlinOptions.isUsages)) continue;
TextRange rangeInElement = ref.getRangeInElement();
processor.process(
new UsageInfo(ref.getElement(), rangeInElement.getStartOffset(), rangeInElement.getEndOffset(), false)
);
}
}
return true;
}
private static DeclarationDescriptor getCallDescriptor(PsiElement element, BindingContext bindingContext) {
JetConstructorCalleeExpression constructorCalleeExpression =
PsiTreeUtil.getParentOfType(element, JetConstructorCalleeExpression.class);
if (constructorCalleeExpression != null) {
JetReferenceExpression classReference = constructorCalleeExpression.getConstructorReferenceExpression();
return bindingContext.get(BindingContext.REFERENCE_TARGET, classReference);
}
JetCallExpression callExpression = PsiTreeUtil.getParentOfType(element, JetCallExpression.class);
if (callExpression != null) {
JetExpression callee = callExpression.getCalleeExpression();
if (callee instanceof JetReferenceExpression) {
return bindingContext.get(BindingContext.REFERENCE_TARGET, (JetReferenceExpression) callee);
}
}
return null;
}
private static boolean isConstructorUsage(PsiElement element, JetClass jetClass) {
PsiConstructorCall constructorCall = PsiTreeUtil.getParentOfType(element, PsiConstructorCall.class);
if (constructorCall != null && constructorCall == element.getParent()) {
PsiMethod constructor = constructorCall.resolveConstructor();
if (constructor == null) return false;
PsiClass constructorClass = constructor.getContainingClass();
return constructorClass != null && constructorClass.getNavigationElement() == jetClass;
}
if (!(element instanceof JetElement)) return false;
BindingContext bindingContext =
AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) element.getContainingFile()).getBindingContext();
DeclarationDescriptor descriptor = getCallDescriptor(element, bindingContext);
if (!(descriptor instanceof ConstructorDescriptor)) return false;
DeclarationDescriptor containingDescriptor = descriptor.getContainingDeclaration();
return containingDescriptor != null
&& BindingContextUtils.descriptorToDeclaration(bindingContext, containingDescriptor) == jetClass;
}
@Override
protected boolean isSearchForTextOccurencesAvailable(@NotNull PsiElement psiElement, boolean isSingleFile) {
if (isSingleFile) return false;
@@ -5,7 +5,23 @@ import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
public class KotlinClassFindUsagesOptions extends JavaClassFindUsagesOptions {
public boolean searchConstructorUsages = true;
public KotlinClassFindUsagesOptions(@NotNull Project project) {
super(project);
}
@Override
public boolean equals(Object o) {
return super.equals(o)
&& o instanceof KotlinClassFindUsagesOptions
&& ((KotlinClassFindUsagesOptions) o).searchConstructorUsages == searchConstructorUsages;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (searchConstructorUsages ? 1 : 0);
return result;
}
}
@@ -0,0 +1,6 @@
// PSI_ELEMENT: org.jetbrains.jet.lang.psi.JetClass
// OPTIONS: usages, constructorUsages
package testing
class <caret>Server() {
}
@@ -0,0 +1,13 @@
import testing.*;
class Client {
private Server myServer = new Server();
public void setServer(Server server) {
myServer = server;
}
public Server getMyServer() {
return myServer;
}
}
@@ -0,0 +1,4 @@
Field declaration (4: 13) private Server myServer = new Server();
Method parameter declaration (6: 27) public void setServer(Server server) {
Method return type (10: 12) public Server getMyServer() {
New instance creation (4: 35) private Server myServer = new Server();
@@ -0,0 +1,6 @@
// PSI_ELEMENT: org.jetbrains.jet.lang.psi.JetClass
// OPTIONS: constructorUsages
package testing
class <caret>Server() {
}
@@ -0,0 +1,13 @@
import testing.*;
class Client {
private Server myServer = new Server();
public void setServer(Server server) {
myServer = server;
}
public Server getMyServer() {
return myServer;
}
}
@@ -0,0 +1 @@
New instance creation (4: 35) private Server myServer = new Server();
@@ -1,4 +1,5 @@
// PSI_ELEMENT: org.jetbrains.jet.lang.psi.JetClass
// OPTIONS: usages
package testing
class <caret>Server() {
@@ -0,0 +1,13 @@
import testing.*;
class Client {
private Server myServer = new Server();
public void setServer(Server server) {
myServer = server;
}
public Server getMyServer() {
return myServer;
}
}
@@ -0,0 +1,3 @@
Field declaration (4: 13) private Server myServer = new Server();
Method parameter declaration (6: 27) public void setServer(Server server) {
Method return type (10: 12) public Server getMyServer() {
@@ -1,5 +0,0 @@
import testing.*;
class Client {
private Server myServer;
}
@@ -1 +0,0 @@
Field declaration (4: 13) private Server myServer;
@@ -0,0 +1,13 @@
// PSI_ELEMENT: org.jetbrains.jet.lang.psi.JetClass
// OPTIONS: usages, constructorUsages
package server
open class <caret>Server {
class object {
val NAME = "Server"
}
open fun work() {
println("Server")
}
}
@@ -2,8 +2,8 @@ package client
import server.Server
class Client: Server {
var nextServer: Server? = null
class Client: Server() {
var nextServer: Server? = new Server()
val name = Server.NAME
fun foo(s: Server) {
@@ -21,6 +21,10 @@ class Client: Server {
}
}
object ClientObject: Server() {
}
fun Client.bar(s: Server) {
foo(s)
}
@@ -0,0 +1,13 @@
Class/object property type (6: 21) var nextServer: Server? = new Server()
Function return types (14: 26) fun getNextServer(): Server? {
Function return types (36: 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 (28: 19) fun Client.bar(s: Server) {
Parameter type (9: 16) fun foo(s: Server) {
Super type qualifier (19: 15) super<Server>.work()
Target type of 'as' operation (37: 40) return if (this is Server) this as Server else null
Target type of 'is' operation (37: 24) return if (this is Server) this as Server else null
Unclassified usage (24: 22) object ClientObject: Server() {
Unclassified usage (5: 15) class Client: Server() {
@@ -1,4 +1,5 @@
// PSI_ELEMENT: org.jetbrains.jet.lang.psi.JetClass
// OPTIONS: usages, constructorUsages
package server
annotation class <caret>X(val x: String)
@@ -1,4 +1,5 @@
// PSI_ELEMENT: org.jetbrains.jet.lang.psi.JetClass
// OPTIONS: usages, constructorUsages
package server
data class <caret>Data
@@ -0,0 +1,13 @@
// PSI_ELEMENT: org.jetbrains.jet.lang.psi.JetClass
// OPTIONS: constructorUsages
package server
open class <caret>Server {
class object {
val NAME = "Server"
}
open fun work() {
println("Server")
}
}
@@ -0,0 +1,38 @@
package client
import server.Server
class Client: Server() {
var nextServer: Server? = new Server()
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")
}
}
object ClientObject: Server() {
}
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,2 @@
Unclassified usage (24: 22) object ClientObject: Server() {
Unclassified usage (5: 15) class Client: Server() {
@@ -1,4 +1,5 @@
// PSI_ELEMENT: org.jetbrains.jet.lang.psi.JetClass
// OPTIONS: usages
package server
open class <caret>Server {
@@ -0,0 +1,38 @@
package client
import server.Server
class Client: Server() {
var nextServer: Server? = new Server()
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")
}
}
object ClientObject: Server() {
}
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
}
@@ -1,12 +1,11 @@
Class/object property type (6: 21) var nextServer: Server? = null
Class/object property type (6: 21) var nextServer: Server? = new Server()
Function return types (14: 26) fun getNextServer(): Server? {
Function return types (32: 21) fun Any.asServer(): Server? {
Function return types (36: 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 (28: 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
Target type of 'as' operation (37: 40) return if (this is Server) this as Server else null
Target type of 'is' operation (37: 24) return if (this is Server) this as Server else null
@@ -1,5 +1,6 @@
// PSI_ELEMENT: org.jetbrains.jet.lang.psi.JetClass
// FILTERING_RULES: org.jetbrains.jet.plugin.findUsages.JetImportFilteringRule
// OPTIONS: usages, constructorUsages
package testing
@@ -1,4 +1,5 @@
// PSI_ELEMENT: org.jetbrains.jet.lang.psi.JetClass
// OPTIONS: usages, constructorUsages
open class <caret>Foo {
vv open val foo = 1
}
@@ -44,9 +44,11 @@ import com.intellij.util.CommonProcessors;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.InTextDirectivesUtils;
import org.jetbrains.jet.lang.psi.JetClass;
import org.jetbrains.jet.lang.psi.JetNamedFunction;
import org.jetbrains.jet.plugin.JetLightProjectDescriptor;
import org.jetbrains.jet.plugin.PluginTestCaseBase;
import org.jetbrains.jet.plugin.findUsages.options.KotlinClassFindUsagesOptions;
import org.jetbrains.jet.plugin.findUsages.options.KotlinMethodFindUsagesOptions;
import java.io.File;
@@ -58,6 +60,26 @@ import java.util.List;
public abstract class AbstractJetFindUsagesTest extends LightCodeInsightFixtureTestCase {
protected static enum OptionsParser {
CLASS {
@NotNull
@Override
public FindUsagesOptions parse(@NotNull String text, @NotNull Project project) {
KotlinClassFindUsagesOptions options = new KotlinClassFindUsagesOptions(project);
options.isUsages = false;
options.searchConstructorUsages = false;
for (String s : InTextDirectivesUtils.findListWithPrefixes(text, "// OPTIONS: ")) {
if (s.equals("usages")) {
options.isUsages = true;
}
else if (s.equals("constructorUsages")) {
options.searchConstructorUsages = true;
}
else fail("Invalid option: " + s);
}
return options;
}
},
METHOD {
@NotNull
@Override
@@ -68,16 +90,15 @@ public abstract class AbstractJetFindUsagesTest extends LightCodeInsightFixtureT
if (s.equals("usages")) {
options.isUsages = true;
}
if (s.equals("overrides")) {
else if (s.equals("overrides")) {
options.isOverridingMethods = true;
options.isImplementingMethods = true;
}
if (s.equals("overloadUsages")) {
else if (s.equals("overloadUsages")) {
options.isIncludeOverloadUsages = true;
options.isUsages = true;
}
else fail("Invalid option: " + s);
}
return options;
@@ -92,6 +113,9 @@ public abstract class AbstractJetFindUsagesTest extends LightCodeInsightFixtureT
if (klass == JetNamedFunction.class) {
return METHOD;
}
if (klass == JetClass.class) {
return CLASS;
}
return null;
}
@@ -43,24 +43,44 @@ public class JetFindUsagesTest extends AbstractJetFindUsagesTest {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("idea/testData/findUsages/findClassUsages"), Pattern.compile("^(.+).0.kt$"), true);
}
@TestMetadata("javaClassUsages.0.kt")
public void testJavaClassUsages() throws Exception {
doTest("idea/testData/findUsages/findClassUsages/javaClassUsages.0.kt");
@TestMetadata("javaClassAllUsages.0.kt")
public void testJavaClassAllUsages() throws Exception {
doTest("idea/testData/findUsages/findClassUsages/javaClassAllUsages.0.kt");
}
@TestMetadata("kotlinClassUsages.0.kt")
public void testKotlinClassUsages() throws Exception {
doTest("idea/testData/findUsages/findClassUsages/kotlinClassUsages.0.kt");
@TestMetadata("javaClassConstructorUsages.0.kt")
public void testJavaClassConstructorUsages() throws Exception {
doTest("idea/testData/findUsages/findClassUsages/javaClassConstructorUsages.0.kt");
}
@TestMetadata("kotlinClassUsages2.0.kt")
public void testKotlinClassUsages2() throws Exception {
doTest("idea/testData/findUsages/findClassUsages/kotlinClassUsages2.0.kt");
@TestMetadata("javaClassNonConstructorUsages.0.kt")
public void testJavaClassNonConstructorUsages() throws Exception {
doTest("idea/testData/findUsages/findClassUsages/javaClassNonConstructorUsages.0.kt");
}
@TestMetadata("kotlinClassUsages3.0.kt")
public void testKotlinClassUsages3() throws Exception {
doTest("idea/testData/findUsages/findClassUsages/kotlinClassUsages3.0.kt");
@TestMetadata("kotlinClassAllUsages.0.kt")
public void testKotlinClassAllUsages() throws Exception {
doTest("idea/testData/findUsages/findClassUsages/kotlinClassAllUsages.0.kt");
}
@TestMetadata("kotlinClassAllUsages2.0.kt")
public void testKotlinClassAllUsages2() throws Exception {
doTest("idea/testData/findUsages/findClassUsages/kotlinClassAllUsages2.0.kt");
}
@TestMetadata("kotlinClassAllUsages3.0.kt")
public void testKotlinClassAllUsages3() throws Exception {
doTest("idea/testData/findUsages/findClassUsages/kotlinClassAllUsages3.0.kt");
}
@TestMetadata("kotlinClassConstructorUsages.0.kt")
public void testKotlinClassConstructorUsages() throws Exception {
doTest("idea/testData/findUsages/findClassUsages/kotlinClassConstructorUsages.0.kt");
}
@TestMetadata("kotlinClassNonConstructorUsages.0.kt")
public void testKotlinClassNonConstructorUsages() throws Exception {
doTest("idea/testData/findUsages/findClassUsages/kotlinClassNonConstructorUsages.0.kt");
}
}