From 5af4101f7d5930976c4d929075d3d15dc3e55796 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Thu, 17 Sep 2015 16:27:54 +0300 Subject: [PATCH] Resource Bundles: Implement references on Kotlin string literals #KT-6946 In Progress --- .../kotlin/psi/psiUtil/jetPsiUtil.kt | 2 + .../kotlin/test/InTextDirectivesUtils.java | 4 +- .../kotlin/generators/tests/GenerateTests.kt | 1 + .../kotlin/idea/findUsages/UsageTypeUtils.kt | 24 ++- idea/src/META-INF/plugin.xml | 1 + .../idea/findUsages/JetUsageTypeProvider.kt | 2 + .../KotlinPropertiesReferenceContributor.kt | 37 +++++ .../idea/properties/referenceProviders.kt | 142 ++++++++++++++++++ .../propertyFileUsagesByRef.0.kt | 9 ++ .../propertyFileUsagesByRef.1.java | 11 ++ .../propertyFileUsagesByRef.2.properties | 1 + .../propertyFileUsagesByRef.3.kt | 5 + .../propertyFileUsagesByRef.results.txt | 2 + .../propertyFiles/propertyUsagesByRef.0.kt | 9 ++ .../propertyFiles/propertyUsagesByRef.1.java | 5 + .../propertyUsagesByRef.2.properties | 1 + .../propertyFiles/propertyUsagesByRef.3.kt | 5 + .../propertyUsagesByRef.results.txt | 2 + .../propertyFileUsages.0.properties | 2 + .../propertyFiles/propertyFileUsages.1.kt | 10 ++ .../propertyFiles/propertyFileUsages.2.kt | 5 + .../propertyFileUsages.results.txt | 2 + .../propertyFiles/propertyUsages.0.properties | 1 + .../propertyFiles/propertyUsages.1.kt | 30 ++++ .../propertyFiles/propertyUsages.2.java | 8 + .../propertyFiles/propertyUsages.3.kt | 5 + .../propertyFiles/propertyUsages.results.txt | 10 ++ .../findUsages/AbstractJetFindUsagesTest.java | 22 ++- .../JetFindUsagesTestGenerated.java | 42 ++++++ 29 files changed, 380 insertions(+), 20 deletions(-) create mode 100644 idea/src/org/jetbrains/kotlin/idea/properties/KotlinPropertiesReferenceContributor.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/properties/referenceProviders.kt create mode 100644 idea/testData/findUsages/kotlin/propertyFiles/propertyFileUsagesByRef.0.kt create mode 100644 idea/testData/findUsages/kotlin/propertyFiles/propertyFileUsagesByRef.1.java create mode 100644 idea/testData/findUsages/kotlin/propertyFiles/propertyFileUsagesByRef.2.properties create mode 100644 idea/testData/findUsages/kotlin/propertyFiles/propertyFileUsagesByRef.3.kt create mode 100644 idea/testData/findUsages/kotlin/propertyFiles/propertyFileUsagesByRef.results.txt create mode 100644 idea/testData/findUsages/kotlin/propertyFiles/propertyUsagesByRef.0.kt create mode 100644 idea/testData/findUsages/kotlin/propertyFiles/propertyUsagesByRef.1.java create mode 100644 idea/testData/findUsages/kotlin/propertyFiles/propertyUsagesByRef.2.properties create mode 100644 idea/testData/findUsages/kotlin/propertyFiles/propertyUsagesByRef.3.kt create mode 100644 idea/testData/findUsages/kotlin/propertyFiles/propertyUsagesByRef.results.txt create mode 100644 idea/testData/findUsages/propertyFiles/propertyFileUsages.0.properties create mode 100644 idea/testData/findUsages/propertyFiles/propertyFileUsages.1.kt create mode 100644 idea/testData/findUsages/propertyFiles/propertyFileUsages.2.kt create mode 100644 idea/testData/findUsages/propertyFiles/propertyFileUsages.results.txt create mode 100644 idea/testData/findUsages/propertyFiles/propertyUsages.0.properties create mode 100644 idea/testData/findUsages/propertyFiles/propertyUsages.1.kt create mode 100644 idea/testData/findUsages/propertyFiles/propertyUsages.2.java create mode 100644 idea/testData/findUsages/propertyFiles/propertyUsages.3.kt create mode 100644 idea/testData/findUsages/propertyFiles/propertyUsages.results.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt index 41db8ad8b59..9b041292439 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt @@ -405,3 +405,5 @@ public fun JetDeclaration.visibilityModifier(): PsiElement? { public fun JetDeclaration.visibilityModifierType(): JetModifierKeywordToken? = visibilityModifier()?.node?.elementType as JetModifierKeywordToken? + +public fun JetStringTemplateExpression.isPlain() = entries.all { it is JetLiteralStringTemplateEntry } \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/test/InTextDirectivesUtils.java b/compiler/tests/org/jetbrains/kotlin/test/InTextDirectivesUtils.java index 9d07268c6f8..ae204920561 100644 --- a/compiler/tests/org/jetbrains/kotlin/test/InTextDirectivesUtils.java +++ b/compiler/tests/org/jetbrains/kotlin/test/InTextDirectivesUtils.java @@ -158,7 +158,7 @@ public final class InTextDirectivesUtils { List resultPrefixes = Lists.newArrayList(); for (String prefix : prefixes) { - if (prefix.startsWith("//")) { + if (prefix.startsWith("//") || prefix.startsWith("##")) { resultPrefixes.add(StringUtil.trimLeading(prefix.substring(2))); } else { @@ -181,7 +181,7 @@ public final class InTextDirectivesUtils { while ((line = reader.readLine()) != null) { line = line.trim(); - if (line.startsWith("//")) { + if (line.startsWith("//") || line.startsWith("##")) { String uncommentedLine = line.substring(2).trim(); if (!uncommentedLine.isEmpty()) { result.add(uncommentedLine); diff --git a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index 5eb61547756..3d4bd9195fe 100644 --- a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -525,6 +525,7 @@ fun main(args: Array) { testClass(javaClass()) { model("findUsages/kotlin", pattern = """^(.+)\.0\.kt$""") model("findUsages/java", pattern = """^(.+)\.0\.java$""") + model("findUsages/propertyFiles", pattern = """^(.+)\.0\.properties$""") } testClass(javaClass()) { diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/findUsages/UsageTypeUtils.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/findUsages/UsageTypeUtils.kt index f5e7a2841e6..1c9a7ca31f0 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/findUsages/UsageTypeUtils.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/findUsages/UsageTypeUtils.kt @@ -17,22 +17,17 @@ package org.jetbrains.kotlin.idea.findUsages import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.lexer.JetTokens -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.descriptors.ConstructorDescriptor -import org.jetbrains.kotlin.descriptors.ClassifierDescriptor -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.ClassKind -import org.jetbrains.kotlin.descriptors.PackageViewDescriptor import com.intellij.psi.PsiPackage -import com.intellij.psi.PsiReferenceExpression -import org.jetbrains.kotlin.descriptors.VariableDescriptor -import org.jetbrains.kotlin.idea.findUsages.UsageTypeEnum.* +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.findUsages.UsageTypeEnum.* import org.jetbrains.kotlin.idea.references.* +import org.jetbrains.kotlin.lexer.JetTokens import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.* +import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType +import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch +import org.jetbrains.kotlin.psi.psiUtil.isAncestor +import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorUtils public object UsageTypeUtils { @@ -41,6 +36,7 @@ public object UsageTypeUtils { is JetForExpression -> return IMPLICIT_ITERATION is JetMultiDeclaration -> return READ is JetPropertyDelegate -> return PROPERTY_DELEGATION + is JetStringTemplateExpression -> return USAGE_IN_STRING_LITERAL } val refExpr = element?.getNonStrictParentOfType() @@ -249,5 +245,7 @@ enum class UsageTypeEnum { CLASS_CAST_TO, ANNOTATION, CLASS_NEW_OPERATOR, - NAMED_ARGUMENT + NAMED_ARGUMENT, + + USAGE_IN_STRING_LITERAL } diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 71cd5183fb4..53a61b4ae30 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -361,6 +361,7 @@ + UsageType.ANNOTATION CLASS_NEW_OPERATOR -> UsageType.CLASS_NEW_OPERATOR NAMED_ARGUMENT -> JetUsageTypes.NAMED_ARGUMENT + + USAGE_IN_STRING_LITERAL -> UsageType.LITERAL_USAGE } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/properties/KotlinPropertiesReferenceContributor.kt b/idea/src/org/jetbrains/kotlin/idea/properties/KotlinPropertiesReferenceContributor.kt new file mode 100644 index 00000000000..43762317514 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/properties/KotlinPropertiesReferenceContributor.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2015 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.kotlin.idea.properties + +import com.intellij.patterns.PlatformPatterns +import com.intellij.psi.PsiReferenceContributor +import com.intellij.psi.PsiReferenceRegistrar +import org.jetbrains.kotlin.psi.JetLiteralStringTemplateEntry +import org.jetbrains.kotlin.psi.JetStringTemplateExpression + +public class KotlinPropertiesReferenceContributor : PsiReferenceContributor() { + override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) { + registrar.registerReferenceProvider( + PlatformPatterns.psiElement(JetStringTemplateExpression::class.java), + KotlinPropertyKeyReferenceProvider + ) + + registrar.registerReferenceProvider( + PlatformPatterns.psiElement(JetStringTemplateExpression::class.java), + KotlinResourceBundleNameReferenceProvider + ) + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/properties/referenceProviders.kt b/idea/src/org/jetbrains/kotlin/idea/properties/referenceProviders.kt new file mode 100644 index 00000000000..6420d69c00d --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/properties/referenceProviders.kt @@ -0,0 +1,142 @@ +/* + * Copyright 2010-2015 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.kotlin.idea.properties + +import com.intellij.codeInsight.AnnotationUtil +import com.intellij.lang.properties.ResourceBundleReference +import com.intellij.lang.properties.references.PropertyReference +import com.intellij.psi.ElementManipulators +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiReference +import com.intellij.psi.PsiReferenceProvider +import com.intellij.psi.search.LocalSearchScope +import com.intellij.util.ProcessingContext +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.ConstructorDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor +import org.jetbrains.kotlin.idea.imports.importableFqName +import org.jetbrains.kotlin.idea.references.mainReference +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType +import org.jetbrains.kotlin.psi.psiUtil.isPlain +import org.jetbrains.kotlin.resolve.calls.callUtil.getParentResolvedCall +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch +import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver + +private val PROPERTY_KEY = FqName(AnnotationUtil.PROPERTY_KEY) +private val PROPERTY_KEY_RESOURCE_BUNDLE = Name.identifier(AnnotationUtil.PROPERTY_KEY_RESOURCE_BUNDLE_PARAMETER) + +private fun AnnotationDescriptor.getBundleName(): String? { + return allValueArguments.entrySet().singleOrNull { it.key.name == PROPERTY_KEY_RESOURCE_BUNDLE }?.value?.value as? String +} + +private fun DeclarationDescriptor.getBundleNameByAnnotation(): String? { + return (annotations.findAnnotation(PROPERTY_KEY) ?: annotations.findExternalAnnotation(PROPERTY_KEY))?.getBundleName() +} + +private fun JetExpression.getBundleNameByContext(): String? { + val expression = JetPsiUtil.safeDeparenthesize(this) + val parent = expression.parent + + (parent as? JetProperty)?.let { return it.resolveToDescriptor().getBundleNameByAnnotation() } + + val bindingContext = expression.analyze(BodyResolveMode.PARTIAL) + val resolvedCall = + if (parent is JetQualifiedExpression && expression == parent.receiverExpression) { + parent.selectorExpression.getResolvedCall(bindingContext) + } + else { + expression.getParentResolvedCall(bindingContext) + } ?: return null + val callable = resolvedCall.resultingDescriptor + + if ((resolvedCall.extensionReceiver as? ExpressionReceiver)?.expression == expression) { + val returnTypeAnnotations = callable.returnType?.annotations ?: return null + return Annotations + .findUseSiteTargetedAnnotation(returnTypeAnnotations, AnnotationUseSiteTarget.RECEIVER, PROPERTY_KEY) + ?.getBundleName() + } + + return resolvedCall.valueArguments.entrySet() + .singleOrNull { it.value.arguments.any { it.getArgumentExpression() == expression } } + ?.key + ?.getBundleNameByAnnotation() +} + +private fun JetAnnotationEntry.getPropertyKeyResolvedCall(): ResolvedCall<*>? { + val resolvedCall = getResolvedCall(analyze(BodyResolveMode.PARTIAL)) ?: return null + val klass = (resolvedCall.resultingDescriptor as? ConstructorDescriptor)?.containingDeclaration ?: return null + if (klass.kind != ClassKind.ANNOTATION_CLASS || klass.importableFqName != PROPERTY_KEY) return null + return resolvedCall +} + +private fun JetStringTemplateExpression.isBundleName(): Boolean { + val parent = JetPsiUtil.safeDeparenthesize(this).parent + when (parent) { + is JetValueArgument -> { + val resolvedCall = parent.getStrictParentOfType()?.getPropertyKeyResolvedCall() ?: return false + val valueParameter = (resolvedCall.getArgumentMapping(parent) as? ArgumentMatch)?.valueParameter ?: return false + if (valueParameter.name != PROPERTY_KEY_RESOURCE_BUNDLE) return false + + return true + } + + is JetProperty -> { + val contexts = (parent.useScope as? LocalSearchScope)?.scope ?: arrayOf(parent.containingFile) + return contexts.any { + it.anyDescendantOfType f@ { entry -> + if (!entry.valueArguments.any { it.getArgumentName()?.asName == PROPERTY_KEY_RESOURCE_BUNDLE }) return@f false + val resolvedCall = entry.getPropertyKeyResolvedCall() ?: return@f false + val parameter = resolvedCall.resultingDescriptor.valueParameters.singleOrNull { it.name == PROPERTY_KEY_RESOURCE_BUNDLE } + ?: return@f false + val valueArgument = resolvedCall.valueArguments[parameter] as? ExpressionValueArgument ?: return@f false + val bundleNameExpression = valueArgument.valueArgument?.getArgumentExpression() ?: return@f false + bundleNameExpression is JetSimpleNameExpression && bundleNameExpression.mainReference.resolve() == parent + } + } + } + } + + return false +} + +public object KotlinPropertyKeyReferenceProvider : PsiReferenceProvider() { + override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array { + if (!(element is JetStringTemplateExpression && element.isPlain())) return PsiReference.EMPTY_ARRAY + val bundleName = element.getBundleNameByContext() ?: return PsiReference.EMPTY_ARRAY + return arrayOf(PropertyReference(ElementManipulators.getValueText(element), element, bundleName, false)) + } +} + +public object KotlinResourceBundleNameReferenceProvider : PsiReferenceProvider() { + override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array { + if (!(element is JetStringTemplateExpression && element.isPlain() && element.isBundleName())) return PsiReference.EMPTY_ARRAY + return arrayOf(ResourceBundleReference(element)) + } +} \ No newline at end of file diff --git a/idea/testData/findUsages/kotlin/propertyFiles/propertyFileUsagesByRef.0.kt b/idea/testData/findUsages/kotlin/propertyFiles/propertyFileUsagesByRef.0.kt new file mode 100644 index 00000000000..26c81bd60fc --- /dev/null +++ b/idea/testData/findUsages/kotlin/propertyFiles/propertyFileUsagesByRef.0.kt @@ -0,0 +1,9 @@ +// PSI_ELEMENT: com.intellij.lang.properties.psi.PropertiesFile +// FIND_BY_REF +import org.jetbrains.annotations.PropertyKey + +public fun message(@PropertyKey(resourceBundle = "idea.testData.findUsages.kotlin.propertyFiles.propertyFileUsagesByRef.2") key: String) = key + +fun test() { + message("foo.bar") +} \ No newline at end of file diff --git a/idea/testData/findUsages/kotlin/propertyFiles/propertyFileUsagesByRef.1.java b/idea/testData/findUsages/kotlin/propertyFiles/propertyFileUsagesByRef.1.java new file mode 100644 index 00000000000..d9f9290f791 --- /dev/null +++ b/idea/testData/findUsages/kotlin/propertyFiles/propertyFileUsagesByRef.1.java @@ -0,0 +1,11 @@ +import org.jetbrains.annotations.PropertyKey; + +class A { + static String message(@PropertyKey(resourceBundle = "idea.testData.findUsages.kotlin.propertyFiles.propertyFileUsagesByRef.2") String key, Object... args) { + return key; + } + + void test() { + message("foo.bar"); + } +} \ No newline at end of file diff --git a/idea/testData/findUsages/kotlin/propertyFiles/propertyFileUsagesByRef.2.properties b/idea/testData/findUsages/kotlin/propertyFiles/propertyFileUsagesByRef.2.properties new file mode 100644 index 00000000000..4cf119bb659 --- /dev/null +++ b/idea/testData/findUsages/kotlin/propertyFiles/propertyFileUsagesByRef.2.properties @@ -0,0 +1 @@ +foo.bar = test \ No newline at end of file diff --git a/idea/testData/findUsages/kotlin/propertyFiles/propertyFileUsagesByRef.3.kt b/idea/testData/findUsages/kotlin/propertyFiles/propertyFileUsagesByRef.3.kt new file mode 100644 index 00000000000..1e1932b2d0f --- /dev/null +++ b/idea/testData/findUsages/kotlin/propertyFiles/propertyFileUsagesByRef.3.kt @@ -0,0 +1,5 @@ +package org.jetbrains.annotations + +@Retention(AnnotationRetention.BINARY) +@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.LOCAL_VARIABLE, AnnotationTarget.FIELD) +public annotation class PropertyKey(public val resourceBundle: String) \ No newline at end of file diff --git a/idea/testData/findUsages/kotlin/propertyFiles/propertyFileUsagesByRef.results.txt b/idea/testData/findUsages/kotlin/propertyFiles/propertyFileUsagesByRef.results.txt new file mode 100644 index 00000000000..870386fd047 --- /dev/null +++ b/idea/testData/findUsages/kotlin/propertyFiles/propertyFileUsagesByRef.results.txt @@ -0,0 +1,2 @@ +[propertyFileUsagesByRef.0.kt] Usage in string constants (5: 51) public fun message(@PropertyKey(resourceBundle = "idea.testData.findUsages.kotlin.propertyFiles.propertyFileUsagesByRef.2") key: String) = key +[propertyFileUsagesByRef.1.java] Method parameter declaration (4: 58) static String message(@PropertyKey(resourceBundle = "idea.testData.findUsages.kotlin.propertyFiles.propertyFileUsagesByRef.2") String key, Object... args) { \ No newline at end of file diff --git a/idea/testData/findUsages/kotlin/propertyFiles/propertyUsagesByRef.0.kt b/idea/testData/findUsages/kotlin/propertyFiles/propertyUsagesByRef.0.kt new file mode 100644 index 00000000000..df0b1f2572d --- /dev/null +++ b/idea/testData/findUsages/kotlin/propertyFiles/propertyUsagesByRef.0.kt @@ -0,0 +1,9 @@ +// PSI_ELEMENT: com.intellij.lang.properties.psi.Property +// FIND_BY_REF +import org.jetbrains.annotations.PropertyKey + +public fun message(@PropertyKey(resourceBundle = "idea.testData.findUsages.kotlin.propertyFiles.propertyUsagesByRef.2") key: String) = key + +fun test() { + message("foo.bar") +} \ No newline at end of file diff --git a/idea/testData/findUsages/kotlin/propertyFiles/propertyUsagesByRef.1.java b/idea/testData/findUsages/kotlin/propertyFiles/propertyUsagesByRef.1.java new file mode 100644 index 00000000000..ff6bb6bec6b --- /dev/null +++ b/idea/testData/findUsages/kotlin/propertyFiles/propertyUsagesByRef.1.java @@ -0,0 +1,5 @@ +class A { + void test() { + PropertyUsages_1Kt.message("foo.bar"); + } +} \ No newline at end of file diff --git a/idea/testData/findUsages/kotlin/propertyFiles/propertyUsagesByRef.2.properties b/idea/testData/findUsages/kotlin/propertyFiles/propertyUsagesByRef.2.properties new file mode 100644 index 00000000000..4cf119bb659 --- /dev/null +++ b/idea/testData/findUsages/kotlin/propertyFiles/propertyUsagesByRef.2.properties @@ -0,0 +1 @@ +foo.bar = test \ No newline at end of file diff --git a/idea/testData/findUsages/kotlin/propertyFiles/propertyUsagesByRef.3.kt b/idea/testData/findUsages/kotlin/propertyFiles/propertyUsagesByRef.3.kt new file mode 100644 index 00000000000..1e1932b2d0f --- /dev/null +++ b/idea/testData/findUsages/kotlin/propertyFiles/propertyUsagesByRef.3.kt @@ -0,0 +1,5 @@ +package org.jetbrains.annotations + +@Retention(AnnotationRetention.BINARY) +@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.LOCAL_VARIABLE, AnnotationTarget.FIELD) +public annotation class PropertyKey(public val resourceBundle: String) \ No newline at end of file diff --git a/idea/testData/findUsages/kotlin/propertyFiles/propertyUsagesByRef.results.txt b/idea/testData/findUsages/kotlin/propertyFiles/propertyUsagesByRef.results.txt new file mode 100644 index 00000000000..646f7c8c781 --- /dev/null +++ b/idea/testData/findUsages/kotlin/propertyFiles/propertyUsagesByRef.results.txt @@ -0,0 +1,2 @@ +[propertyUsagesByRef.0.kt] Usage in string constants (8: 14) message("foo.bar") +[propertyUsagesByRef.1.java] Usage in string constants (3: 37) PropertyUsages_1Kt.message("foo.bar"); \ No newline at end of file diff --git a/idea/testData/findUsages/propertyFiles/propertyFileUsages.0.properties b/idea/testData/findUsages/propertyFiles/propertyFileUsages.0.properties new file mode 100644 index 00000000000..b69774df5a8 --- /dev/null +++ b/idea/testData/findUsages/propertyFiles/propertyFileUsages.0.properties @@ -0,0 +1,2 @@ +## FIND_FILE_USAGES +foo.bar = test \ No newline at end of file diff --git a/idea/testData/findUsages/propertyFiles/propertyFileUsages.1.kt b/idea/testData/findUsages/propertyFiles/propertyFileUsages.1.kt new file mode 100644 index 00000000000..889999c1ab9 --- /dev/null +++ b/idea/testData/findUsages/propertyFiles/propertyFileUsages.1.kt @@ -0,0 +1,10 @@ +import org.jetbrains.annotations.PropertyKey + +private val BUNDLE_NAME = "idea.testData.findUsages.propertyFiles.propertyFileUsages.0" + +public fun message(@PropertyKey(resourceBundle = "idea.testData.findUsages.propertyFiles.propertyFileUsages.0") key: String) = key +public fun message2(@PropertyKey(resourceBundle = BUNDLE_NAME) key: String) = key + +fun test() { + message("foo.bar") +} \ No newline at end of file diff --git a/idea/testData/findUsages/propertyFiles/propertyFileUsages.2.kt b/idea/testData/findUsages/propertyFiles/propertyFileUsages.2.kt new file mode 100644 index 00000000000..1e1932b2d0f --- /dev/null +++ b/idea/testData/findUsages/propertyFiles/propertyFileUsages.2.kt @@ -0,0 +1,5 @@ +package org.jetbrains.annotations + +@Retention(AnnotationRetention.BINARY) +@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.LOCAL_VARIABLE, AnnotationTarget.FIELD) +public annotation class PropertyKey(public val resourceBundle: String) \ No newline at end of file diff --git a/idea/testData/findUsages/propertyFiles/propertyFileUsages.results.txt b/idea/testData/findUsages/propertyFiles/propertyFileUsages.results.txt new file mode 100644 index 00000000000..98cd8cb87c1 --- /dev/null +++ b/idea/testData/findUsages/propertyFiles/propertyFileUsages.results.txt @@ -0,0 +1,2 @@ +Usage in string constants (3: 28) private val BUNDLE_NAME = "idea.testData.findUsages.propertyFiles.propertyFileUsages.0" +Usage in string constants (5: 51) public fun message(@PropertyKey(resourceBundle = "idea.testData.findUsages.propertyFiles.propertyFileUsages.0") key: String) = key \ No newline at end of file diff --git a/idea/testData/findUsages/propertyFiles/propertyUsages.0.properties b/idea/testData/findUsages/propertyFiles/propertyUsages.0.properties new file mode 100644 index 00000000000..570d2cf8e3f --- /dev/null +++ b/idea/testData/findUsages/propertyFiles/propertyUsages.0.properties @@ -0,0 +1 @@ +foo.bar = test \ No newline at end of file diff --git a/idea/testData/findUsages/propertyFiles/propertyUsages.1.kt b/idea/testData/findUsages/propertyFiles/propertyUsages.1.kt new file mode 100644 index 00000000000..2ce13a13d24 --- /dev/null +++ b/idea/testData/findUsages/propertyFiles/propertyUsages.1.kt @@ -0,0 +1,30 @@ +import org.jetbrains.annotations.PropertyKey + +public fun message(@PropertyKey(resourceBundle = "idea.testData.findUsages.propertyFiles.propertyUsages.0") key: String) = key + +public fun String.infixMessage(@PropertyKey(resourceBundle = "idea.testData.findUsages.propertyFiles.propertyUsages.0") key: String) = key + +public fun @receiver:PropertyKey(resourceBundle = "idea.testData.findUsages.propertyFiles.propertyUsages.0") String.infixMessage2(s: String) = this + +public fun @receiver:PropertyKey(resourceBundle = "idea.testData.findUsages.propertyFiles.propertyUsages.0") String.minus() = this + +public fun Int.get(@PropertyKey(resourceBundle = "idea.testData.findUsages.propertyFiles.propertyUsages.0") key: String) = this + +public fun @receiver:PropertyKey(resourceBundle = "idea.testData.findUsages.propertyFiles.propertyUsages.0") String.get(s: String) = this + +fun test() { + @PropertyKey(resourceBundle = "idea.testData.findUsages.propertyFiles.propertyUsages.0") val s1 = "foo.bar" + @PropertyKey(resourceBundle = "idea.testData.findUsages.propertyFiles.propertyUsages.0") val s2 = "foo.baz" + message("foo.bar") + message("foo.baz") + + "test" infixMessage "foo.bar" + "foo.bar" infixMessage "test" + "foo.bar" infixMessage2 "test" + "test" infixMessage2 "foo.bar" + "foo.bar".infixMessage2("test") + -"foo.bar" + 1["foo.bar"] + "foo.bar"["test"] + "test"["foo.bar"] +} \ No newline at end of file diff --git a/idea/testData/findUsages/propertyFiles/propertyUsages.2.java b/idea/testData/findUsages/propertyFiles/propertyUsages.2.java new file mode 100644 index 00000000000..e3902ffd6ab --- /dev/null +++ b/idea/testData/findUsages/propertyFiles/propertyUsages.2.java @@ -0,0 +1,8 @@ +class A { + void test() { + @PropertyKey(resourceBundle = "idea.testData.findUsages.propertyFiles.propertyUsages.0") String s1 = "foo.bar" + @PropertyKey(resourceBundle = "idea.testData.findUsages.propertyFiles.propertyUsages.0") String s2 = "foo.baz" + PropertyUsages_1Kt.message("foo.bar"); + PropertyUsages_1Kt.message("foo.baz"); + } +} \ No newline at end of file diff --git a/idea/testData/findUsages/propertyFiles/propertyUsages.3.kt b/idea/testData/findUsages/propertyFiles/propertyUsages.3.kt new file mode 100644 index 00000000000..1e1932b2d0f --- /dev/null +++ b/idea/testData/findUsages/propertyFiles/propertyUsages.3.kt @@ -0,0 +1,5 @@ +package org.jetbrains.annotations + +@Retention(AnnotationRetention.BINARY) +@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.LOCAL_VARIABLE, AnnotationTarget.FIELD) +public annotation class PropertyKey(public val resourceBundle: String) \ No newline at end of file diff --git a/idea/testData/findUsages/propertyFiles/propertyUsages.results.txt b/idea/testData/findUsages/propertyFiles/propertyUsages.results.txt new file mode 100644 index 00000000000..b2026e8818b --- /dev/null +++ b/idea/testData/findUsages/propertyFiles/propertyUsages.results.txt @@ -0,0 +1,10 @@ +[propertyUsages.1.kt] Usage in string constants (16: 104) @PropertyKey(resourceBundle = "idea.testData.findUsages.propertyFiles.propertyUsages.0") val s1 = "foo.bar" +[propertyUsages.1.kt] Usage in string constants (18: 14) message("foo.bar") +[propertyUsages.1.kt] Usage in string constants (21: 26) "test" infixMessage "foo.bar" +[propertyUsages.1.kt] Usage in string constants (23: 6) "foo.bar" infixMessage2 "test" +[propertyUsages.1.kt] Usage in string constants (25: 6) "foo.bar".infixMessage2("test") +[propertyUsages.1.kt] Usage in string constants (26: 7) -"foo.bar" +[propertyUsages.1.kt] Usage in string constants (27: 8) 1["foo.bar"] +[propertyUsages.1.kt] Usage in string constants (28: 6) "foo.bar"["test"] +[propertyUsages.2.java] Usage in string constants (3: 111) @PropertyKey(resourceBundle = "idea.testData.findUsages.propertyFiles.propertyUsages.0") String s1 = "foo.bar" +[propertyUsages.2.java] Usage in string constants (5: 37) PropertyUsages_1Kt.message("foo.bar"); \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/findUsages/AbstractJetFindUsagesTest.java b/idea/tests/org/jetbrains/kotlin/findUsages/AbstractJetFindUsagesTest.java index 622e0d95f9d..45bbd7f05ad 100644 --- a/idea/tests/org/jetbrains/kotlin/findUsages/AbstractJetFindUsagesTest.java +++ b/idea/tests/org/jetbrains/kotlin/findUsages/AbstractJetFindUsagesTest.java @@ -22,11 +22,12 @@ import com.google.common.base.Predicates; import com.google.common.collect.Collections2; import com.google.common.collect.Ordering; import com.intellij.codeInsight.JavaTargetElementEvaluator; -import com.intellij.codeInsight.TargetElementUtil; import com.intellij.codeInsight.TargetElementUtilBase; import com.intellij.find.FindManager; import com.intellij.find.findUsages.*; import com.intellij.find.impl.FindManagerImpl; +import com.intellij.lang.properties.psi.PropertiesFile; +import com.intellij.lang.properties.psi.Property; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.io.FileUtil; @@ -296,11 +297,21 @@ public abstract class AbstractJetFindUsagesTest extends JetLightCodeInsightFixtu String mainFileText = FileUtil.loadFile(mainFile, true); final String prefix = mainFileName.substring(0, mainFileName.indexOf('.') + 1); - List caretElementClassNames = InTextDirectivesUtils.findLinesWithPrefixesRemoved(mainFileText, "// PSI_ELEMENT: "); - assert caretElementClassNames.size() == 1; - //noinspection unchecked + boolean isPropertiesFile = FileUtilRt.getExtension(path).equals("properties"); - Class caretElementClass = (Class)Class.forName(caretElementClassNames.get(0)); + Class caretElementClass; + if (!isPropertiesFile) { + List caretElementClassNames = InTextDirectivesUtils.findLinesWithPrefixesRemoved(mainFileText, "// PSI_ELEMENT: "); + assert caretElementClassNames.size() == 1; + //noinspection unchecked + caretElementClass = (Class)Class.forName(caretElementClassNames.get(0)); + } + else { + //noinspection unchecked + caretElementClass = (Class) (InTextDirectivesUtils.isDirectiveDefined(mainFileText, "## FIND_FILE_USAGES") + ? PropertiesFile.class + : Property.class); + } OptionsParser parser = OptionsParser.getParserByPsiElementClass(caretElementClass); @@ -317,6 +328,7 @@ public abstract class AbstractJetFindUsagesTest extends JetLightCodeInsightFixtu return ext.equals("kt") || ext.equals("java") || ext.equals("xml") + || ext.equals("properties") || (ext.equals("txt") && !name.endsWith(".results.txt")); } } diff --git a/idea/tests/org/jetbrains/kotlin/findUsages/JetFindUsagesTestGenerated.java b/idea/tests/org/jetbrains/kotlin/findUsages/JetFindUsagesTestGenerated.java index a5889561747..e053e0cbf11 100644 --- a/idea/tests/org/jetbrains/kotlin/findUsages/JetFindUsagesTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/findUsages/JetFindUsagesTestGenerated.java @@ -1036,6 +1036,27 @@ public class JetFindUsagesTestGenerated extends AbstractJetFindUsagesTest { } } + @TestMetadata("idea/testData/findUsages/kotlin/propertyFiles") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class PropertyFiles extends AbstractJetFindUsagesTest { + public void testAllFilesPresentInPropertyFiles() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/findUsages/kotlin/propertyFiles"), Pattern.compile("^(.+)\\.0\\.kt$"), true); + } + + @TestMetadata("propertyFileUsagesByRef.0.kt") + public void testPropertyFileUsagesByRef() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/propertyFiles/propertyFileUsagesByRef.0.kt"); + doTest(fileName); + } + + @TestMetadata("propertyUsagesByRef.0.kt") + public void testPropertyUsagesByRef() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/propertyFiles/propertyUsagesByRef.0.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/findUsages/kotlin/unresolvedAnnotation") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) @@ -1315,4 +1336,25 @@ public class JetFindUsagesTestGenerated extends AbstractJetFindUsagesTest { } } } + + @TestMetadata("idea/testData/findUsages/propertyFiles") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class PropertyFiles extends AbstractJetFindUsagesTest { + public void testAllFilesPresentInPropertyFiles() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/findUsages/propertyFiles"), Pattern.compile("^(.+)\\.0\\.properties$"), true); + } + + @TestMetadata("propertyFileUsages.0.properties") + public void testPropertyFileUsages() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/findUsages/propertyFiles/propertyFileUsages.0.properties"); + doTest(fileName); + } + + @TestMetadata("propertyUsages.0.properties") + public void testPropertyUsages() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/findUsages/propertyFiles/propertyUsages.0.properties"); + doTest(fileName); + } + } }