diff --git a/generators/org/jetbrains/jet/generators/tests/GenerateTests.java b/generators/org/jetbrains/jet/generators/tests/GenerateTests.java index 1866fe39408..763233fed39 100644 --- a/generators/org/jetbrains/jet/generators/tests/GenerateTests.java +++ b/generators/org/jetbrains/jet/generators/tests/GenerateTests.java @@ -460,6 +460,12 @@ public class GenerateTests { testModelWithFileName( "idea/testData/findUsages/findMethodUsages/kotlinMethodUsages", "Server.kt", "testFindMethodKotlinUsages" ), + testModelWithFileName( + "idea/testData/findUsages/findMethodUsages/javaAndKotlinOverrides", "Server.kt", "testFindMethodJavaUsages" + ), + testModelWithFileName( + "idea/testData/findUsages/findMethodUsages/kotlinOverloadUsages", "Server.kt", "testFindMethodKotlinUsages" + ), testModelWithFileName( "idea/testData/findUsages/findPropertyUsages/javaPropertyUsages", "Server.kt", "testFindPropertyJavaUsages" ), diff --git a/idea/src/org/jetbrains/jet/plugin/JetBundle.properties b/idea/src/org/jetbrains/jet/plugin/JetBundle.properties index 827068bbfe2..3d2721e659d 100644 --- a/idea/src/org/jetbrains/jet/plugin/JetBundle.properties +++ b/idea/src/org/jetbrains/jet/plugin/JetBundle.properties @@ -265,4 +265,8 @@ choose.the.ones.you.want.to.be.deleted=Choose the ones you want to be deleted. method.column=Member delete.param.in.method.hierarchy={0} is a part of method hierarchy. Do you want to delete multiple parameters? super.methods.delete.with.usage.search=delete (with usage search) -super.methods.action.key.find.usages=find usages of \ No newline at end of file +super.methods.action.key.find.usages=find usages of + +find.what.implementing.methods.checkbox=&Implementing functions +find.what.overriding.methods.checkbox=Over&riding functions +find.options.include.overloaded.methods.checkbox=Include o&verloaded functions and extensions \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/plugin/findUsages/KotlinFindUsagesHandlerFactory.java b/idea/src/org/jetbrains/jet/plugin/findUsages/KotlinFindUsagesHandlerFactory.java index 19beafda416..713af945a7e 100644 --- a/idea/src/org/jetbrains/jet/plugin/findUsages/KotlinFindUsagesHandlerFactory.java +++ b/idea/src/org/jetbrains/jet/plugin/findUsages/KotlinFindUsagesHandlerFactory.java @@ -18,10 +18,12 @@ package org.jetbrains.jet.plugin.findUsages; import com.intellij.find.findUsages.FindUsagesHandler; import com.intellij.find.findUsages.FindUsagesHandlerFactory; +import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.psi.JetClass; import org.jetbrains.jet.lang.psi.JetDeclaration; +import org.jetbrains.jet.plugin.findUsages.options.KotlinMethodFindUsagesOptions; import org.jetbrains.jet.lang.psi.JetNamedFunction; import org.jetbrains.jet.plugin.findUsages.handlers.KotlinFindClassUsagesHandler; import org.jetbrains.jet.plugin.findUsages.handlers.KotlinFindFunctionUsagesHandler; @@ -30,6 +32,16 @@ import org.jetbrains.jet.plugin.refactoring.JetRefactoringUtil; import java.util.Collection; public class KotlinFindUsagesHandlerFactory extends FindUsagesHandlerFactory { + private final KotlinMethodFindUsagesOptions findMethodOptions; + + public KotlinFindUsagesHandlerFactory(@NotNull Project project) { + findMethodOptions = new KotlinMethodFindUsagesOptions(project); + } + + public final KotlinMethodFindUsagesOptions getFindMethodOptions() { + return findMethodOptions; + } + @Override public boolean canFindUsages(@NotNull PsiElement element) { return element instanceof JetClass || element instanceof JetNamedFunction; diff --git a/idea/src/org/jetbrains/jet/plugin/findUsages/dialogs/KotlinFindMethodUsagesDialog.java b/idea/src/org/jetbrains/jet/plugin/findUsages/dialogs/KotlinFindMethodUsagesDialog.java new file mode 100644 index 00000000000..7fc335131a6 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/findUsages/dialogs/KotlinFindMethodUsagesDialog.java @@ -0,0 +1,91 @@ +package org.jetbrains.jet.plugin.findUsages.dialogs; + +import com.intellij.find.FindBundle; +import com.intellij.find.FindSettings; +import com.intellij.find.findUsages.FindMethodUsagesDialog; +import com.intellij.find.findUsages.FindUsagesHandler; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiMethod; +import com.intellij.ui.SimpleColoredComponent; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.plugin.JetBundle; +import org.jetbrains.jet.plugin.findUsages.options.KotlinMethodFindUsagesOptions; +import org.jetbrains.jet.plugin.refactoring.JetRefactoringUtil; + +import javax.swing.*; +import java.awt.*; + +public class KotlinFindMethodUsagesDialog extends FindMethodUsagesDialog { + public KotlinFindMethodUsagesDialog( + PsiMethod method, + Project project, + KotlinMethodFindUsagesOptions findUsagesOptions, + boolean toShowInNewTab, + boolean mustOpenInNewTab, + boolean isSingleFile, + FindUsagesHandler handler + ) { + super(method, project, findUsagesOptions, toShowInNewTab, mustOpenInNewTab, isSingleFile, handler); + } + + @Override + protected KotlinMethodFindUsagesOptions getFindUsagesOptions() { + return (KotlinMethodFindUsagesOptions) super.getFindUsagesOptions(); + } + + @Override + public void configureLabelComponent(@NotNull SimpleColoredComponent coloredComponent) { + coloredComponent.append(JetRefactoringUtil.formatJavaOrLightMethod((PsiMethod) myPsiElement)); + } + + private static boolean renameCheckbox(@NotNull JPanel panel, @NotNull String srcText, @NotNull String destText) { + for (Component component : panel.getComponents()) { + if (component instanceof JCheckBox) { + JCheckBox checkBox = (JCheckBox) component; + if (checkBox.getText().equals(srcText)) { + checkBox.setText(destText); + return true; + } + } + } + + return false; + } + + @Override + protected JPanel createFindWhatPanel() { + JPanel findWhatPanel = super.createFindWhatPanel(); + if (findWhatPanel != null) { + renameCheckbox( + findWhatPanel, + FindBundle.message("find.what.implementing.methods.checkbox"), + JetBundle.message("find.what.implementing.methods.checkbox") + ); + renameCheckbox( + findWhatPanel, + FindBundle.message("find.what.overriding.methods.checkbox"), + JetBundle.message("find.what.overriding.methods.checkbox") + ); + } + + return findWhatPanel; + } + + @Override + protected void addUsagesOptions(JPanel optionsPanel) { + super.addUsagesOptions(optionsPanel); + + if (!renameCheckbox( + optionsPanel, + FindBundle.message("find.options.include.overloaded.methods.checkbox"), + JetBundle.message("find.options.include.overloaded.methods.checkbox") + )) { + addCheckboxToPanel( + JetBundle.message("find.options.include.overloaded.methods.checkbox"), + FindSettings.getInstance().isSearchOverloadedMethods(), + optionsPanel, + false + ); + } + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/findUsages/handlers/KotlinFindFunctionUsagesHandler.java b/idea/src/org/jetbrains/jet/plugin/findUsages/handlers/KotlinFindFunctionUsagesHandler.java index 3012b1f159e..3ffeb2604e7 100644 --- a/idea/src/org/jetbrains/jet/plugin/findUsages/handlers/KotlinFindFunctionUsagesHandler.java +++ b/idea/src/org/jetbrains/jet/plugin/findUsages/handlers/KotlinFindFunctionUsagesHandler.java @@ -16,9 +16,27 @@ 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.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ReadActionProcessor; +import com.intellij.openapi.util.Computable; +import com.intellij.psi.*; +import com.intellij.psi.search.PsiElementProcessor; +import com.intellij.psi.search.PsiElementProcessorAdapter; +import com.intellij.psi.search.SearchScope; +import com.intellij.psi.search.searches.MethodReferencesSearch; +import com.intellij.psi.search.searches.OverridingMethodsSearch; +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.JetNamedFunction; import org.jetbrains.jet.plugin.findUsages.KotlinFindUsagesHandlerFactory; +import org.jetbrains.jet.plugin.findUsages.dialogs.KotlinFindMethodUsagesDialog; +import org.jetbrains.jet.plugin.findUsages.options.KotlinMethodFindUsagesOptions; import java.util.Collection; @@ -34,4 +52,90 @@ public class KotlinFindFunctionUsagesHandler extends KotlinFindUsagesHandler processor, + @NotNull FindUsagesOptions options + ) { + final KotlinMethodFindUsagesOptions kotlinOptions = (KotlinMethodFindUsagesOptions)options; + SearchScope searchScope = kotlinOptions.searchScope; + + final PsiMethod lightMethod = ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public PsiMethod compute() { + return LightClassUtil.getLightClassMethod(getElement()); + } + }); + if (lightMethod == null) return true; + + if (kotlinOptions.isUsages) { + boolean strictSignatureSearch = !kotlinOptions.isIncludeOverloadUsages; + if (!MethodReferencesSearch + .search( + new MethodReferencesSearch.SearchParameters( + lightMethod, searchScope, strictSignatureSearch, kotlinOptions.fastTrack + ) + ) + .forEach( + new ReadActionProcessor() { + @Override + public boolean processInReadAction(PsiReference ref) { + return processUsage(processor, ref, kotlinOptions); + } + } + )) { + return false; + } + } + + boolean isAbstract = ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public Boolean compute() { + return lightMethod.hasModifierProperty(PsiModifier.ABSTRACT); + } + }); + + if (isAbstract && kotlinOptions.isImplementingMethods || kotlinOptions.isOverridingMethods) { + OverridingMethodsSearch.search(lightMethod, options.searchScope, kotlinOptions.isCheckDeepInheritance).forEach( + new PsiElementProcessorAdapter( + new PsiElementProcessor() { + @Override + public boolean execute(@NotNull PsiMethod element) { + return processUsage(processor, element.getNavigationElement(), kotlinOptions); + } + } + ) + ); + } + + return true; + } + + @Override + protected boolean isSearchForTextOccurencesAvailable(@NotNull PsiElement psiElement, boolean isSingleFile) { + return psiElement instanceof JetNamedFunction || psiElement instanceof PsiMethod; + } + + @NotNull + @Override + public FindUsagesOptions getFindUsagesOptions(@Nullable DataContext dataContext) { + return getFactory().getFindMethodOptions(); + } + + } diff --git a/idea/src/org/jetbrains/jet/plugin/findUsages/handlers/KotlinFindUsagesHandler.java b/idea/src/org/jetbrains/jet/plugin/findUsages/handlers/KotlinFindUsagesHandler.java index 714de4c8d68..921432eb363 100644 --- a/idea/src/org/jetbrains/jet/plugin/findUsages/handlers/KotlinFindUsagesHandler.java +++ b/idea/src/org/jetbrains/jet/plugin/findUsages/handlers/KotlinFindUsagesHandler.java @@ -17,10 +17,20 @@ package org.jetbrains.jet.plugin.findUsages.handlers; import com.intellij.find.findUsages.FindUsagesHandler; -import com.intellij.find.findUsages.JavaFindUsagesHandlerFactory; +import com.intellij.find.findUsages.FindUsagesOptions; +import com.intellij.find.findUsages.JavaFindUsagesOptions; +import com.intellij.openapi.application.ReadActionProcessor; +import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.SearchScope; +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.lang.psi.JetImportDirective; import org.jetbrains.jet.plugin.findUsages.KotlinFindUsagesHandlerFactory; import java.util.Collection; @@ -54,6 +64,30 @@ public abstract class KotlinFindUsagesHandler extends Find return factory; } + protected static boolean filterUsage(@NotNull PsiElement usage, @NotNull FindUsagesOptions options) { + if (options instanceof JavaFindUsagesOptions && ((JavaFindUsagesOptions) options).isSkipImportStatements) { + if (PsiTreeUtil.getParentOfType(usage, JetImportDirective.class) != null) return false; + } + + return true; + } + + protected static boolean processUsage(Processor processor, PsiReference ref, FindUsagesOptions options) { + if (filterUsage(ref.getElement(), options)){ + TextRange rangeInElement = ref.getRangeInElement(); + return processor.process(new UsageInfo(ref.getElement(), rangeInElement.getStartOffset(), rangeInElement.getEndOffset(), false)); + } + return true; + } + + protected static boolean processUsage( + @NotNull Processor processor, + @NotNull PsiElement element, + @NotNull FindUsagesOptions options + ) { + return !filterUsage(element, options) || processor.process(new UsageInfo(element)); + } + @NotNull @Override public PsiElement[] getPrimaryElements() { @@ -61,4 +95,60 @@ public abstract class KotlinFindUsagesHandler extends Find ? new PsiElement[] {getPsiElement()} : elementsToSearch.toArray(new PsiElement[elementsToSearch.size()]); } + + protected boolean searchReferences( + @NotNull PsiElement element, + @NotNull final Processor processor, + @NotNull FindUsagesOptions options + ) { + if (options.isUsages) { + boolean success = ReferencesSearch.search( + new ReferencesSearch.SearchParameters(element, options.searchScope, false, options.fastTrack) + ).forEach(new ReadActionProcessor() { + @Override + public boolean processInReadAction(PsiReference ref) { + TextRange rangeInElement = ref.getRangeInElement(); + return processor.process( + new UsageInfo(ref.getElement(), rangeInElement.getStartOffset(), rangeInElement.getEndOffset(), false) + ); + } + }); + if (!success) return false; + } + return true; + } + + protected boolean searchTextOccurrences( + @NotNull final PsiElement element, + @NotNull final Processor processor, + @NotNull FindUsagesOptions options + ) { + final SearchScope scope = options.searchScope; + + boolean searchText = options.isSearchForTextOccurrences && scope instanceof GlobalSearchScope; + + if (searchText) { + if (options.fastTrack != null) { + options.fastTrack.searchCustom(new Processor>() { + @Override + public boolean process(Processor consumer) { + return processUsagesInText(element, processor, (GlobalSearchScope)scope); + } + }); + } + else { + return processUsagesInText(element, processor, (GlobalSearchScope)scope); + } + } + return true; + } + + @Override + public boolean processElementUsages( + @NotNull PsiElement element, + @NotNull Processor processor, + @NotNull FindUsagesOptions options + ) { + return searchReferences(element, processor, options) && searchTextOccurrences(element, processor, options); + } } diff --git a/idea/src/org/jetbrains/jet/plugin/findUsages/options/KotlinMethodFindUsagesOptions.java b/idea/src/org/jetbrains/jet/plugin/findUsages/options/KotlinMethodFindUsagesOptions.java new file mode 100644 index 00000000000..cad7ed7b82a --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/findUsages/options/KotlinMethodFindUsagesOptions.java @@ -0,0 +1,11 @@ +package org.jetbrains.jet.plugin.findUsages.options; + +import com.intellij.find.findUsages.JavaMethodFindUsagesOptions; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; + +public class KotlinMethodFindUsagesOptions extends JavaMethodFindUsagesOptions { + public KotlinMethodFindUsagesOptions(@NotNull Project project) { + super(project); + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/references/JetPsiReference.java b/idea/src/org/jetbrains/jet/plugin/references/JetPsiReference.java index 60f5fd56dac..81cea61310f 100644 --- a/idea/src/org/jetbrains/jet/plugin/references/JetPsiReference.java +++ b/idea/src/org/jetbrains/jet/plugin/references/JetPsiReference.java @@ -29,6 +29,7 @@ import org.jetbrains.jet.lang.psi.JetFile; import org.jetbrains.jet.lang.psi.JetReferenceExpression; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingContextUtils; +import org.jetbrains.jet.lang.resolve.java.JetClsMethod; import org.jetbrains.jet.plugin.project.WholeProjectAnalyzerFacade; import java.util.ArrayList; @@ -86,7 +87,8 @@ public abstract class JetPsiReference implements PsiPolyVariantReference { @Override public boolean isReferenceTo(PsiElement element) { PsiElement target = resolve(); - return target == element || target != null && target.getNavigationElement() == element; + PsiElement mirrorElement = element instanceof JetClsMethod ? ((JetClsMethod) element).getOrigin() : null; + return target == element || (mirrorElement != null && target == mirrorElement) || (target != null && target.getNavigationElement() == element); } @NotNull diff --git a/idea/testData/findUsages/findMethodUsages/javaAndKotlinOverrides/Client.java b/idea/testData/findUsages/findMethodUsages/javaAndKotlinOverrides/Client.java new file mode 100644 index 00000000000..35f1880b2d3 --- /dev/null +++ b/idea/testData/findUsages/findMethodUsages/javaAndKotlinOverrides/Client.java @@ -0,0 +1,6 @@ +class C extends A { + @Override + public void foo(String s) { + super.foo(s); + } +} \ No newline at end of file diff --git a/idea/testData/findUsages/findMethodUsages/javaAndKotlinOverrides/Server.kt b/idea/testData/findUsages/findMethodUsages/javaAndKotlinOverrides/Server.kt new file mode 100644 index 00000000000..eef1b48efb5 --- /dev/null +++ b/idea/testData/findUsages/findMethodUsages/javaAndKotlinOverrides/Server.kt @@ -0,0 +1,39 @@ +// OPTIONS: overrides +open class A { + open fun foo(t: T) { + println(t) + } + + open fun foo(t: T, tt: T) { + println(t) + } +} + +fun A.foo(t: T, x: String) { + foo(t) + println(x) +} + +fun A.foo(s: String, n: Number) { + fun A.foo(t: T, x: String) { + foo(t) + println(x) + } + + foo(s) + println(n) +} + +open class B: A() { + override fun foo(t: String) { + super.foo(t) + } + + open fun baz(a: A) { + a.foo("", 0) + } + + open fun baz(a: A) { + a.foo(0, "") + } +} \ No newline at end of file diff --git a/idea/testData/findUsages/findMethodUsages/javaAndKotlinOverrides/results.txt b/idea/testData/findUsages/findMethodUsages/javaAndKotlinOverrides/results.txt new file mode 100644 index 00000000000..15296fd993b --- /dev/null +++ b/idea/testData/findUsages/findMethodUsages/javaAndKotlinOverrides/results.txt @@ -0,0 +1,2 @@ +Unclassified usage (28: 18) override fun foo(t: String) { +Unclassified usage (3: 17) public void foo(String s) { \ No newline at end of file diff --git a/idea/testData/findUsages/findMethodUsages/javaMethodUsages/Server.kt b/idea/testData/findUsages/findMethodUsages/javaMethodUsages/Server.kt index 66bd72bc977..31c8f264f7f 100644 --- a/idea/testData/findUsages/findMethodUsages/javaMethodUsages/Server.kt +++ b/idea/testData/findUsages/findMethodUsages/javaMethodUsages/Server.kt @@ -1,3 +1,4 @@ +// OPTIONS: usages package testing; public class Server() { diff --git a/idea/testData/findUsages/findMethodUsages/kotlinMethodUsages/Server.kt b/idea/testData/findUsages/findMethodUsages/kotlinMethodUsages/Server.kt index 4c303b09795..5312755d6de 100644 --- a/idea/testData/findUsages/findMethodUsages/kotlinMethodUsages/Server.kt +++ b/idea/testData/findUsages/findMethodUsages/kotlinMethodUsages/Server.kt @@ -1,3 +1,4 @@ +// OPTIONS: usages package server; fun processRequest() = "foo" diff --git a/idea/testData/findUsages/findMethodUsages/kotlinOverloadUsages/Client.kt b/idea/testData/findUsages/findMethodUsages/kotlinOverloadUsages/Client.kt new file mode 100644 index 00000000000..e45aaebf15f --- /dev/null +++ b/idea/testData/findUsages/findMethodUsages/kotlinOverloadUsages/Client.kt @@ -0,0 +1,13 @@ +open class B: A() { + override fun foo(t: String) { + super.foo(t) + } + + open fun baz(a: A) { + a.foo("", 0) + } + + open fun baz(a: A) { + a.foo(0, "") + } +} \ No newline at end of file diff --git a/idea/testData/findUsages/findMethodUsages/kotlinOverloadUsages/Server.kt b/idea/testData/findUsages/findMethodUsages/kotlinOverloadUsages/Server.kt new file mode 100644 index 00000000000..a77dac1af0b --- /dev/null +++ b/idea/testData/findUsages/findMethodUsages/kotlinOverloadUsages/Server.kt @@ -0,0 +1,25 @@ +// OPTIONS: overloadUsages +open class A { + open fun foo(t: T) { + println(t) + } + + open fun foo(t: T, tt: T) { + println(t) + } +} + +fun A.foo(t: T, x: String) { + foo(t) + println(x) +} + +fun A.foo(s: String, n: Number) { + fun A.foo(t: T, x: String) { + foo(t) + println(x) + } + + foo(s) + println(n) +} \ No newline at end of file diff --git a/idea/testData/findUsages/findMethodUsages/kotlinOverloadUsages/results.txt b/idea/testData/findUsages/findMethodUsages/kotlinOverloadUsages/results.txt new file mode 100644 index 00000000000..b435f2d9064 --- /dev/null +++ b/idea/testData/findUsages/findMethodUsages/kotlinOverloadUsages/results.txt @@ -0,0 +1,4 @@ +Function call (13: 5) foo(t) +Function call (19: 9) foo(t) +Function call (23: 5) foo(s) +Function call (3: 18) super.foo(t) \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/findUsages/AbstractJetFindUsagesTest.java b/idea/tests/org/jetbrains/jet/findUsages/AbstractJetFindUsagesTest.java index 57e56488325..239eacb6de8 100644 --- a/idea/tests/org/jetbrains/jet/findUsages/AbstractJetFindUsagesTest.java +++ b/idea/tests/org/jetbrains/jet/findUsages/AbstractJetFindUsagesTest.java @@ -22,7 +22,12 @@ 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.find.FindManager; +import com.intellij.find.findUsages.FindUsagesHandler; +import com.intellij.find.findUsages.FindUsagesOptions; +import com.intellij.find.impl.FindManagerImpl; import com.intellij.openapi.extensions.Extensions; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiComment; @@ -35,8 +40,11 @@ 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 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.JetFunction; import org.jetbrains.jet.lang.psi.JetObjectDeclarationName; @@ -44,6 +52,7 @@ 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; +import org.jetbrains.jet.plugin.findUsages.options.KotlinMethodFindUsagesOptions; import java.io.File; import java.io.IOException; @@ -52,6 +61,37 @@ import java.util.Collection; import java.util.Collections; public abstract class AbstractJetFindUsagesTest extends LightCodeInsightFixtureTestCase { + private interface OptionsParser { + @NotNull + FindUsagesOptions parse(@NotNull String text, @NotNull Project project); + } + + private static final OptionsParser METHOD_OPTIONS_PARSER = new OptionsParser() { + @Override + @NotNull + public FindUsagesOptions parse(@NotNull String text, @NotNull Project project) { + KotlinMethodFindUsagesOptions options = new KotlinMethodFindUsagesOptions(project); + options.isUsages = false; + for (String s : InTextDirectivesUtils.findListWithPrefixes(text, "// OPTIONS: ")) { + if (s.equals("usages")) { + options.isUsages = true; + } + + if (s.equals("overrides")) { + options.isOverridingMethods = true; + options.isImplementingMethods = true; + } + + if (s.equals("overloadUsages")) { + options.isIncludeOverloadUsages = true; + options.isUsages = true; + } + } + + return options; + } + }; + @NotNull @Override protected LightProjectDescriptor getProjectDescriptor() { @@ -65,58 +105,60 @@ public abstract class AbstractJetFindUsagesTest extends LightCodeInsightFixtureT } public void testFindClassJavaUsages(@NotNull String path) throws Exception { - doTestWithoutFiltering(path, true, JetClass.class); + doTestWithoutFiltering(path, true, JetClass.class, null); } public void testFindClassKotlinUsages(@NotNull String path) throws Exception { - doTestWithoutFiltering(path, false, JetClass.class); + doTestWithoutFiltering(path, false, JetClass.class, null); } public void testFindUsagesUnresolvedAnnotation(@NotNull String path) throws Exception { - doTestWithoutFiltering(path, true, JetClass.class); + doTestWithoutFiltering(path, true, JetClass.class, null); } public void testFindMethodJavaUsages(@NotNull String path) throws Exception { - doTestWithoutFiltering(path, true, JetFunction.class); + doTestWithoutFiltering(path, true, JetFunction.class, METHOD_OPTIONS_PARSER); } public void testFindMethodKotlinUsages(@NotNull String path) throws Exception { - doTestWithoutFiltering(path, false, JetFunction.class); + doTestWithoutFiltering(path, false, JetFunction.class, METHOD_OPTIONS_PARSER); } public void testFindPropertyJavaUsages(@NotNull String path) throws Exception { - doTestWithoutFiltering(path, true, JetProperty.class); + doTestWithoutFiltering(path, true, JetProperty.class, null); } public void testFindPropertyKotlinUsages(@NotNull String path) throws Exception { - doTestWithoutFiltering(path, false, JetProperty.class); + doTestWithoutFiltering(path, false, JetProperty.class, null); } public void testFindObjectJavaUsages(@NotNull String path) throws Exception { - doTestWithoutFiltering(path, true, JetObjectDeclarationName.class); + doTestWithoutFiltering(path, true, JetObjectDeclarationName.class, null); } public void testFindObjectKotlinUsages(@NotNull String path) throws Exception { - doTestWithoutFiltering(path, false, JetObjectDeclarationName.class); + doTestWithoutFiltering(path, false, JetObjectDeclarationName.class, null); } public void testFindWithFilteringImports(@NotNull String path) throws Exception { - doTest(path, false, JetClass.class, Lists.newArrayList(new JetImportFilteringRule())); + doTest(path, false, JetClass.class, Lists.newArrayList(new JetImportFilteringRule()), null); } private void doTestWithoutFiltering( - String path, + @NotNull String path, boolean searchInJava, - Class caretElementClass + @NotNull Class caretElementClass, + @Nullable OptionsParser parser ) throws Exception { - doTest(path, searchInJava, caretElementClass, Collections.emptyList()); + doTest(path, searchInJava, caretElementClass, Collections.emptyList(), parser); } private void doTest( - String path, + @NotNull String path, boolean searchInJava, - Class caretElementClass, - Collection filters + @NotNull Class caretElementClass, + @NotNull Collection filters, + @Nullable OptionsParser parser ) throws IOException { String rootPath = path.substring(0, path.lastIndexOf("/") + 1); @@ -124,7 +166,8 @@ public abstract class AbstractJetFindUsagesTest extends LightCodeInsightFixtureT T caretElement = PsiTreeUtil.getParentOfType(myFixture.getElementAtCaret(), caretElementClass, false); assertNotNull(String.format("Element with type '%s' wasn't found at caret position", caretElementClass), caretElement); - Collection usageInfos = myFixture.findUsages(caretElement); + FindUsagesOptions options = parser != null ? parser.parse(FileUtil.loadFile(new File(path)), getProject()) : null; + Collection usageInfos = findUsages(caretElement, options); Collection filteredUsages = getUsageAdapters(filters, usageInfos); @@ -138,10 +181,30 @@ public abstract class AbstractJetFindUsagesTest extends LightCodeInsightFixtureT }; Collection finalUsages = Ordering.natural().sortedCopy(Collections2.transform(filteredUsages, convertToString)); - String expectedText = FileUtil.loadFile(new File(rootPath + "results.txt")); + String expectedText = FileUtil.loadFile(new File(rootPath + "results.txt"), true); assertOrderedEquals(finalUsages, Ordering.natural().sortedCopy(StringUtil.split(expectedText, "\n"))); } + private Collection findUsages(@NotNull PsiElement targetElement, @Nullable FindUsagesOptions options) { + Project project = getProject(); + FindUsagesHandler handler = + ((FindManagerImpl) FindManager.getInstance(project)).getFindUsagesManager().getFindUsagesHandler(targetElement, false); + assert handler != null : "Cannot find handler for: " + targetElement; + + if (options == null) { + options = handler.getFindUsagesOptions(null); + } + + CommonProcessors.CollectProcessor processor = new CommonProcessors.CollectProcessor(); + PsiElement[] psiElements = ArrayUtil.mergeArrays(handler.getPrimaryElements(), handler.getSecondaryElements()); + + for (PsiElement psiElement : psiElements) { + handler.processElementUsages(psiElement, processor, options); + } + + return processor.getResults(); + } + private static Collection getUsageAdapters( final Collection filters, Collection usageInfos diff --git a/idea/tests/org/jetbrains/jet/findUsages/JetFindUsagesTest.java b/idea/tests/org/jetbrains/jet/findUsages/JetFindUsagesTest.java index 5637a665378..379077486d0 100644 --- a/idea/tests/org/jetbrains/jet/findUsages/JetFindUsagesTest.java +++ b/idea/tests/org/jetbrains/jet/findUsages/JetFindUsagesTest.java @@ -30,7 +30,7 @@ import org.jetbrains.jet.findUsages.AbstractJetFindUsagesTest; /** This class is generated by {@link org.jetbrains.jet.generators.tests.GenerateTests}. DO NOT MODIFY MANUALLY */ @SuppressWarnings("all") -@InnerTestClasses({JetFindUsagesTest.JavaClassUsages.class, JetFindUsagesTest.KotlinClassUsages1.class, JetFindUsagesTest.KotlinClassUsages2.class, JetFindUsagesTest.KotlinClassUsages3.class, JetFindUsagesTest.JavaObjectUsages.class, JetFindUsagesTest.KotlinObjectUsages.class, JetFindUsagesTest.JavaMethodUsages.class, JetFindUsagesTest.KotlinMethodUsages.class, JetFindUsagesTest.JavaPropertyUsages.class, JetFindUsagesTest.KotlinPropertyUsages.class, JetFindUsagesTest.FindWithFilteringImports.class, JetFindUsagesTest.UnresolvedAnnotation.class}) +@InnerTestClasses({JetFindUsagesTest.JavaClassUsages.class, JetFindUsagesTest.KotlinClassUsages1.class, JetFindUsagesTest.KotlinClassUsages2.class, JetFindUsagesTest.KotlinClassUsages3.class, JetFindUsagesTest.JavaObjectUsages.class, JetFindUsagesTest.KotlinObjectUsages.class, JetFindUsagesTest.JavaMethodUsages.class, JetFindUsagesTest.KotlinMethodUsages.class, JetFindUsagesTest.JavaAndKotlinOverrides.class, JetFindUsagesTest.KotlinOverloadUsages.class, JetFindUsagesTest.JavaPropertyUsages.class, JetFindUsagesTest.KotlinPropertyUsages.class, JetFindUsagesTest.FindWithFilteringImports.class, JetFindUsagesTest.UnresolvedAnnotation.class}) public class JetFindUsagesTest extends AbstractJetFindUsagesTest { @TestMetadata("idea/testData/findUsages/findClassUsages/javaClassUsages") public static class JavaClassUsages extends AbstractJetFindUsagesTest { @@ -136,6 +136,32 @@ public class JetFindUsagesTest extends AbstractJetFindUsagesTest { } + @TestMetadata("idea/testData/findUsages/findMethodUsages/javaAndKotlinOverrides") + public static class JavaAndKotlinOverrides extends AbstractJetFindUsagesTest { + @TestMetadata("Server.kt") + public void test() throws Exception { + testFindMethodJavaUsages("idea/testData/findUsages/findMethodUsages/javaAndKotlinOverrides/Server.kt"); + } + + public void testAllFilesPresentInJavaAndKotlinOverrides() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("idea/testData/findUsages/findMethodUsages/javaAndKotlinOverrides"), Pattern.compile("^(.*)Server.kt$"), true); + } + + } + + @TestMetadata("idea/testData/findUsages/findMethodUsages/kotlinOverloadUsages") + public static class KotlinOverloadUsages extends AbstractJetFindUsagesTest { + @TestMetadata("Server.kt") + public void test() throws Exception { + testFindMethodKotlinUsages("idea/testData/findUsages/findMethodUsages/kotlinOverloadUsages/Server.kt"); + } + + public void testAllFilesPresentInKotlinOverloadUsages() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("idea/testData/findUsages/findMethodUsages/kotlinOverloadUsages"), Pattern.compile("^(.*)Server.kt$"), true); + } + + } + @TestMetadata("idea/testData/findUsages/findPropertyUsages/javaPropertyUsages") public static class JavaPropertyUsages extends AbstractJetFindUsagesTest { @TestMetadata("Server.kt") @@ -198,6 +224,8 @@ public class JetFindUsagesTest extends AbstractJetFindUsagesTest { suite.addTestSuite(KotlinObjectUsages.class); suite.addTestSuite(JavaMethodUsages.class); suite.addTestSuite(KotlinMethodUsages.class); + suite.addTestSuite(JavaAndKotlinOverrides.class); + suite.addTestSuite(KotlinOverloadUsages.class); suite.addTestSuite(JavaPropertyUsages.class); suite.addTestSuite(KotlinPropertyUsages.class); suite.addTestSuite(FindWithFilteringImports.class);