Implement structural grouping of Kotlin declaration usages

#KT-4742 Fixed
This commit is contained in:
Alexey Sedunov
2014-03-25 15:39:40 +04:00
parent 5d4527d7aa
commit cceb66d632
14 changed files with 248 additions and 14 deletions
@@ -0,0 +1,6 @@
<root>
<item
name='com.intellij.usages.impl.FileStructureGroupRuleProvider com.intellij.usages.rules.UsageGroupingRule getUsageGroupingRule(com.intellij.openapi.project.Project) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
</root>
+1
View File
@@ -164,6 +164,7 @@
<lang.foldingBuilder language="jet" implementationClass="org.jetbrains.jet.plugin.JetFoldingBuilder"/>
<lang.formatter language="jet" implementationClass="org.jetbrains.jet.plugin.formatter.JetFormattingModelBuilder"/>
<lang.findUsagesProvider language="jet" implementationClass="org.jetbrains.jet.plugin.findUsages.JetFindUsagesProvider"/>
<fileStructureGroupRuleProvider implementation="org.jetbrains.jet.plugin.findUsages.KotlinDeclarationGroupRuleProvider"/>
<importFilteringRule implementation="org.jetbrains.jet.plugin.findUsages.JetImportFilteringRule"/>
<lang.refactoringSupport language="jet" implementationClass="org.jetbrains.jet.plugin.refactoring.JetRefactoringSupportProvider"/>
<lang.surroundDescriptor language="jet"
@@ -0,0 +1,49 @@
/*
* Copyright 2010-2014 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.rules.PsiElementUsage
import com.intellij.usages.UsageGroup
import com.intellij.usages.Usage
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.usages.rules.UsageGroupingRule
import org.jetbrains.jet.lang.psi.JetNamedDeclaration
import com.intellij.usages.PsiNamedElementUsageGroupBase
import org.jetbrains.jet.lang.psi.JetFile
import com.intellij.usages.impl.FileStructureGroupRuleProvider
import com.intellij.openapi.project.Project
import org.jetbrains.jet.lang.psi.JetNamedFunction
public class KotlinDeclarationGroupRuleProvider : FileStructureGroupRuleProvider {
public class KotlinDeclarationGroupingRule : UsageGroupingRule {
override fun groupUsage(usage: Usage): UsageGroup? {
val element = (usage as? PsiElementUsage)?.getElement()
if (element == null) return null
val containingFile = element.getContainingFile()
if (containingFile !is JetFile) return null
return PsiTreeUtil.getTopmostParentOfType(element, javaClass<JetNamedDeclaration>())?.let { container ->
PsiNamedElementUsageGroupBase(container)
}
}
}
override fun getUsageGroupingRule(project: Project): UsageGroupingRule = KotlinDeclarationGroupingRule()
}
@@ -0,0 +1,14 @@
// PSI_ELEMENT: org.jetbrains.jet.lang.psi.JetClass
// GROUPING_RULES: org.jetbrains.jet.plugin.findUsages.KotlinDeclarationGroupRuleProvider$KotlinDeclarationGroupingRule
// OPTIONS: usages, constructorUsages
package server
open class <caret>Server {
class object {
val NAME = "Server"
}
open fun work() {
println("Server")
}
}
@@ -0,0 +1,42 @@
package client
import server.Server
class Client(name: String = Server.NAME): 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")
}
class object: Server() {
}
}
object ClientObject: Server() {
}
fun Client.bar(s: Server = Server.NAME) {
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,16 @@
Class/object property type (Client) (6: 21) var nextServer: Server? = new Server()
Function return types (Client) (14: 26) fun getNextServer(): Server? {
Function return types (asServer) (40: 21) fun Any.asServer(): Server? {
Local variable declaration (Client) (10: 21) val server: Server = s
Nested class/object (Client) (5: 29) class Client(name: String = Server.NAME): Server() {
Nested class/object (Client) (7: 16) val name = Server.NAME
Nested class/object (bar) (32: 28) fun Client.bar(s: Server = Server.NAME) {
Parameter type (Client) (9: 16) fun foo(s: Server) {
Parameter type (bar) (32: 19) fun Client.bar(s: Server = Server.NAME) {
Super type qualifier (Client) (19: 15) super<Server>.work()
Supertype (Client) (23: 19) class object: Server() {
Supertype (Client) (5: 43) class Client(name: String = Server.NAME): Server() {
Supertype (ClientObject) (28: 22) object ClientObject: Server() {
Target type of 'is' operation (asServer) (41: 24) return if (this is Server) this as Server else null
Usage in cast target type (asServer) (41: 40) return if (this is Server) this as Server else null
Usage in import (3: 15) import server.Server
@@ -0,0 +1,13 @@
// PSI_ELEMENT: org.jetbrains.jet.lang.psi.JetNamedFunction
// GROUPING_RULES: org.jetbrains.jet.plugin.findUsages.KotlinDeclarationGroupRuleProvider$KotlinDeclarationGroupingRule
// OPTIONS: usages
package server;
public open class Server() {
open fun <caret>processRequest() = "foo"
}
public class ServerEx(): Server() {
override fun processRequest() = "foofoo"
}
@@ -0,0 +1,8 @@
import server.*;
class Client {
public fun foo() {
Server().processRequest()
ServerEx().processRequest()
}
}
@@ -0,0 +1,2 @@
Function call (Client) (5: 18) Server().processRequest()
Function call (Client) (6: 20) ServerEx().processRequest()
@@ -0,0 +1,20 @@
// PSI_ELEMENT: org.jetbrains.jet.lang.psi.JetProperty
// GROUPING_RULES: org.jetbrains.jet.plugin.findUsages.KotlinDeclarationGroupRuleProvider$KotlinDeclarationGroupingRule
// OPTIONS: usages
package server;
open class A<T> {
open var <caret>foo: T
}
open class B: A<String>() {
open var foo: String
get() {
println("get")
return super<A>.foo
}
set(value: String) {
println("set:" + value)
super<A>.foo = value
}
}
@@ -0,0 +1,13 @@
import server.*;
class Client {
public fun foo() {
val a = A<String>()
a.foo = "a"
println("a.foo = ${a.foo}")
val b = B()
b.foo = "b"
println("b.foo = ${b.foo}")
}
}
@@ -0,0 +1,6 @@
Value read (B) (14: 29) return super<A>.foo
Value read (Client) (11: 30) println("b.foo = ${b.foo}")
Value read (Client) (7: 30) println("a.foo = ${a.foo}")
Value write (B) (18: 22) super<A>.foo = value
Value write (Client) (10: 11) b.foo = "b"
Value write (Client) (6: 11) a.foo = "a"
@@ -33,13 +33,17 @@ 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.UsageGroup;
import com.intellij.usages.UsageInfo2UsageAdapter;
import com.intellij.usages.UsageViewPresentation;
import com.intellij.usages.impl.rules.UsageType;
import com.intellij.usages.impl.rules.UsageTypeProvider;
import com.intellij.usages.rules.UsageFilteringRule;
import com.intellij.usages.rules.UsageGroupingRule;
import com.intellij.util.ArrayUtil;
import com.intellij.util.CommonProcessors;
import kotlin.Function1;
import kotlin.KotlinPackage;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.InTextDirectivesUtils;
@@ -303,19 +307,38 @@ public abstract class AbstractJetFindUsagesTest extends LightCodeInsightFixtureT
FindUsagesOptions options = parser != null ? parser.parse(mainFileText, getProject()) : null;
Collection<UsageInfo> usageInfos = findUsages(caretElement, options);
Collection<UsageInfo2UsageAdapter> filteredUsages = getUsageAdapters(
getTestFilteringRules(mainFileText, "// FILTERING_RULES: "),
usageInfos);
Collection<UsageFilteringRule> filteringRules = instantiateClasses(mainFileText, "// FILTERING_RULES: ");
final Collection<UsageGroupingRule> groupingRules = instantiateClasses(mainFileText, "// GROUPING_RULES: ");
Collection<UsageInfo2UsageAdapter> filteredUsages = getUsageAdapters(filteringRules, usageInfos);
Function<UsageInfo2UsageAdapter, String> convertToString = new Function<UsageInfo2UsageAdapter, String>() {
@Override
public String apply(@Nullable UsageInfo2UsageAdapter usageAdapter) {
public String apply(@Nullable final UsageInfo2UsageAdapter usageAdapter) {
assert usageAdapter != null;
String groupAsString = Joiner.on(", ").join(
KotlinPackage.map(
groupingRules,
new Function1<UsageGroupingRule, String>() {
@Override
public String invoke(UsageGroupingRule rule) {
UsageGroup group = rule.groupUsage(usageAdapter);
return group != null ? group.getText(null) : "";
}
}
)
);
if (!groupAsString.isEmpty()) {
groupAsString = "(" + groupAsString + ") ";
}
UsageType usageType = getUsageType(usageAdapter.getElement());
String usageTypeAsString = usageType == null ? "null" : usageType.toString(USAGE_VIEW_PRESENTATION);
return usageTypeAsString + " " + Joiner.on("").join(Arrays.asList(usageAdapter.getPresentation().getText()));
return usageTypeAsString + " " +
groupAsString +
Joiner.on("").join(Arrays.asList(usageAdapter.getPresentation().getText()));
}
};
@@ -386,14 +409,14 @@ public abstract class AbstractJetFindUsagesTest extends LightCodeInsightFixtureT
return UsageType.UNCLASSIFIED;
}
private static Collection<UsageFilteringRule> getTestFilteringRules(String mainFileText, String directive)
private static <T> Collection<T> instantiateClasses(String mainFileText, String directive)
throws ClassNotFoundException, InstantiationException, IllegalAccessException {
List<String> filteringRuleClassNames = InTextDirectivesUtils.findLinesWithPrefixesRemoved(mainFileText, directive);
Collection<UsageFilteringRule> filters = new ArrayList<UsageFilteringRule>();
Collection<T> filters = new ArrayList<T>();
for (String filteringRuleClassName : filteringRuleClassNames) {
//noinspection unchecked
Class<UsageFilteringRule> klass = (Class<UsageFilteringRule>) Class.forName(filteringRuleClassName);
Class<T> klass = (Class<T>) Class.forName(filteringRuleClassName);
filters.add(klass.newInstance());
}
@@ -16,24 +16,21 @@
package org.jetbrains.jet.findUsages;
import junit.framework.Assert;
import junit.framework.Test;
import junit.framework.TestSuite;
import java.io.File;
import java.util.regex.Pattern;
import org.jetbrains.jet.JetTestUtils;
import org.jetbrains.jet.test.InnerTestClasses;
import org.jetbrains.jet.test.TestMetadata;
import org.jetbrains.jet.findUsages.AbstractJetFindUsagesTest;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@InnerTestClasses({JetFindUsagesTestGenerated.Kotlin.class, JetFindUsagesTestGenerated.Java.class})
public class JetFindUsagesTestGenerated extends AbstractJetFindUsagesTest {
@TestMetadata("idea/testData/findUsages/kotlin")
@InnerTestClasses({Kotlin.FindClassUsages.class, Kotlin.FindFunctionUsages.class, Kotlin.FindObjectUsages.class, Kotlin.FindParameterUsages.class, Kotlin.FindPropertyUsages.class, Kotlin.FindTypeParameterUsages.class, Kotlin.FindWithFilteringImports.class, Kotlin.UnresolvedAnnotation.class})
@InnerTestClasses({Kotlin.FindClassUsages.class, Kotlin.FindFunctionUsages.class, Kotlin.FindObjectUsages.class, Kotlin.FindParameterUsages.class, Kotlin.FindPropertyUsages.class, Kotlin.FindTypeParameterUsages.class, Kotlin.FindWithFilteringImports.class, Kotlin.FindWithStructuralGrouping.class, Kotlin.UnresolvedAnnotation.class})
public static class Kotlin extends AbstractJetFindUsagesTest {
public void testAllFilesPresentInKotlin() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("idea/testData/findUsages/kotlin"), Pattern.compile("^(.+)\\.0\\.kt$"), true);
@@ -540,6 +537,29 @@ public class JetFindUsagesTestGenerated extends AbstractJetFindUsagesTest {
}
@TestMetadata("idea/testData/findUsages/kotlin/findWithStructuralGrouping")
public static class FindWithStructuralGrouping extends AbstractJetFindUsagesTest {
public void testAllFilesPresentInFindWithStructuralGrouping() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("idea/testData/findUsages/kotlin/findWithStructuralGrouping"), Pattern.compile("^(.+)\\.0\\.kt$"), true);
}
@TestMetadata("kotlinClassAllUsages.0.kt")
public void testKotlinClassAllUsages() throws Exception {
doTest("idea/testData/findUsages/kotlin/findWithStructuralGrouping/kotlinClassAllUsages.0.kt");
}
@TestMetadata("kotlinMethodUsages.0.kt")
public void testKotlinMethodUsages() throws Exception {
doTest("idea/testData/findUsages/kotlin/findWithStructuralGrouping/kotlinMethodUsages.0.kt");
}
@TestMetadata("kotlinPropertyUsages.0.kt")
public void testKotlinPropertyUsages() throws Exception {
doTest("idea/testData/findUsages/kotlin/findWithStructuralGrouping/kotlinPropertyUsages.0.kt");
}
}
@TestMetadata("idea/testData/findUsages/kotlin/unresolvedAnnotation")
public static class UnresolvedAnnotation extends AbstractJetFindUsagesTest {
public void testAllFilesPresentInUnresolvedAnnotation() throws Exception {
@@ -563,6 +583,7 @@ public class JetFindUsagesTestGenerated extends AbstractJetFindUsagesTest {
suite.addTestSuite(FindPropertyUsages.class);
suite.addTestSuite(FindTypeParameterUsages.class);
suite.addTestSuite(FindWithFilteringImports.class);
suite.addTestSuite(FindWithStructuralGrouping.class);
suite.addTestSuite(UnresolvedAnnotation.class);
return suite;
}