Implement structural grouping of Kotlin declaration usages
#KT-4742 Fixed
This commit is contained in:
@@ -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>
|
||||
@@ -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
|
||||
}
|
||||
+16
@@ -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()
|
||||
}
|
||||
}
|
||||
+2
@@ -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}")
|
||||
}
|
||||
}
|
||||
+6
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user