diff --git a/idea/idea-completion/idea-completion.iml b/idea/idea-completion/idea-completion.iml
index 8b06fd2ca4f..ade1db9de2e 100644
--- a/idea/idea-completion/idea-completion.iml
+++ b/idea/idea-completion/idea-completion.iml
@@ -23,5 +23,6 @@
+
\ No newline at end of file
diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KotlinCompletionContributor.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KotlinCompletionContributor.kt
index 268fd3d21ef..274dc6e51ac 100644
--- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KotlinCompletionContributor.kt
+++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KotlinCompletionContributor.kt
@@ -79,6 +79,8 @@ public class KotlinCompletionContributor : CompletionContributor() {
isInUnclosedSuperQualifier(tokenBefore) -> CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED + ">"
+ isInSimpleStringTemplate(tokenBefore) -> CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED
+
else -> specialLambdaSignatureDummyIdentifier(tokenBefore)
?: specialExtensionReceiverDummyIdentifier(tokenBefore)
?: specialInTypeArgsDummyIdentifier(tokenBefore)
@@ -242,6 +244,8 @@ public class KotlinCompletionContributor : CompletionContributor() {
return
}
+ if (PropertyKeyCompletion.perform(parameters, result)) return
+
try {
result.restartCompletionWhenNothingMatches()
@@ -432,4 +436,8 @@ public class KotlinCompletionContributor : CompletionContributor() {
val superToken = ltToken.prevLeaf { it !is PsiWhiteSpace && it !is PsiComment }
return superToken?.node?.elementType == JetTokens.SUPER_KEYWORD
}
+
+ private fun isInSimpleStringTemplate(tokenBefore: PsiElement?): Boolean {
+ return tokenBefore?.parents?.firstIsInstanceOrNull()?.isPlain() ?: false
+ }
}
diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/PropertyKeyCompletion.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/PropertyKeyCompletion.kt
new file mode 100644
index 00000000000..13786446cca
--- /dev/null
+++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/PropertyKeyCompletion.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.completion
+
+import com.intellij.codeInsight.completion.CompletionParameters
+import com.intellij.codeInsight.completion.CompletionResultSet
+import com.intellij.lang.properties.references.PropertiesCompletionContributor
+import com.intellij.lang.properties.references.PropertyReference
+import org.jetbrains.kotlin.psi.JetStringTemplateExpression
+import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
+import org.jetbrains.kotlin.psi.psiUtil.isPlain
+import org.jetbrains.kotlin.psi.psiUtil.startOffset
+import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
+
+object PropertyKeyCompletion {
+ fun perform(parameters: CompletionParameters, result: CompletionResultSet): Boolean {
+ val template = parameters.position.getStrictParentOfType() ?: return false
+ if (!template.isPlain()) return false
+
+ val references = template.references
+ val propertyReference = references.firstIsInstanceOrNull() ?: return false
+ if (PropertiesCompletionContributor.hasMoreImportantReference(references, propertyReference)) return false
+
+ val startOffset = parameters.offset
+ val offsetInElement = startOffset - template.startOffset
+ val range = propertyReference.rangeInElement
+ if (offsetInElement < range.startOffset) return false
+
+ val prefix = template.text.substring(range.startOffset, offsetInElement)
+ val variants = PropertiesCompletionContributor.getVariants(propertyReference)
+ result.withPrefixMatcher(prefix).addAllElements(variants.toList())
+
+ return true
+ }
+}
diff --git a/idea/idea-completion/testData/basic/multifile/PropertyKeysEmptyString/PropertyKeysEmptyString.dependency.kt b/idea/idea-completion/testData/basic/multifile/PropertyKeysEmptyString/PropertyKeysEmptyString.dependency.kt
new file mode 100644
index 00000000000..1e1932b2d0f
--- /dev/null
+++ b/idea/idea-completion/testData/basic/multifile/PropertyKeysEmptyString/PropertyKeysEmptyString.dependency.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/idea-completion/testData/basic/multifile/PropertyKeysEmptyString/PropertyKeysEmptyString.kt b/idea/idea-completion/testData/basic/multifile/PropertyKeysEmptyString/PropertyKeysEmptyString.kt
new file mode 100644
index 00000000000..40cb12de4ac
--- /dev/null
+++ b/idea/idea-completion/testData/basic/multifile/PropertyKeysEmptyString/PropertyKeysEmptyString.kt
@@ -0,0 +1,13 @@
+import org.jetbrains.annotations.PropertyKey
+
+fun message(@PropertyKey(resourceBundle = "PropertyKeysEmptyString") key: String) = key
+
+fun test() {
+ message("")
+}
+
+// EXIST: { lookupString: "foo.bar", itemText: "foo.bar", tailText: "1", typeText: "PropertyKeysEmptyString" }
+// EXIST: { lookupString: "bar.baz", itemText: "bar.baz", tailText: "2", typeText: "PropertyKeysEmptyString" }
+// EXIST: { lookupString: "foo.bar.baz", itemText: "foo.bar.baz", tailText: "3", typeText: "PropertyKeysEmptyString" }
+// EXIST: { lookupString: "foo.test", itemText: "foo.test", tailText: "4", typeText: "PropertyKeysEmptyString" }
+// NOTHING_ELSE
\ No newline at end of file
diff --git a/idea/idea-completion/testData/basic/multifile/PropertyKeysEmptyString/PropertyKeysEmptyString.properties b/idea/idea-completion/testData/basic/multifile/PropertyKeysEmptyString/PropertyKeysEmptyString.properties
new file mode 100644
index 00000000000..5999617526a
--- /dev/null
+++ b/idea/idea-completion/testData/basic/multifile/PropertyKeysEmptyString/PropertyKeysEmptyString.properties
@@ -0,0 +1,4 @@
+foo.bar = 1
+bar.baz = 2
+foo.bar.baz = 3
+foo.test = 4
\ No newline at end of file
diff --git a/idea/idea-completion/testData/basic/multifile/PropertyKeysNoPrefix/PropertyKeysNoPrefix.dependency.kt b/idea/idea-completion/testData/basic/multifile/PropertyKeysNoPrefix/PropertyKeysNoPrefix.dependency.kt
new file mode 100644
index 00000000000..1e1932b2d0f
--- /dev/null
+++ b/idea/idea-completion/testData/basic/multifile/PropertyKeysNoPrefix/PropertyKeysNoPrefix.dependency.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/idea-completion/testData/basic/multifile/PropertyKeysNoPrefix/PropertyKeysNoPrefix.kt b/idea/idea-completion/testData/basic/multifile/PropertyKeysNoPrefix/PropertyKeysNoPrefix.kt
new file mode 100644
index 00000000000..e3412202680
--- /dev/null
+++ b/idea/idea-completion/testData/basic/multifile/PropertyKeysNoPrefix/PropertyKeysNoPrefix.kt
@@ -0,0 +1,13 @@
+import org.jetbrains.annotations.PropertyKey
+
+fun message(@PropertyKey(resourceBundle = "PropertyKeysNoPrefix") key: String) = key
+
+fun test() {
+ message("foo")
+}
+
+// EXIST: { lookupString: "foo.bar", itemText: "foo.bar", tailText: "1", typeText: "PropertyKeysNoPrefix" }
+// EXIST: { lookupString: "bar.baz", itemText: "bar.baz", tailText: "2", typeText: "PropertyKeysNoPrefix" }
+// EXIST: { lookupString: "foo.bar.baz", itemText: "foo.bar.baz", tailText: "3", typeText: "PropertyKeysNoPrefix" }
+// EXIST: { lookupString: "foo.test", itemText: "foo.test", tailText: "4", typeText: "PropertyKeysNoPrefix" }
+// NOTHING_ELSE
\ No newline at end of file
diff --git a/idea/idea-completion/testData/basic/multifile/PropertyKeysNoPrefix/PropertyKeysNoPrefix.properties b/idea/idea-completion/testData/basic/multifile/PropertyKeysNoPrefix/PropertyKeysNoPrefix.properties
new file mode 100644
index 00000000000..5999617526a
--- /dev/null
+++ b/idea/idea-completion/testData/basic/multifile/PropertyKeysNoPrefix/PropertyKeysNoPrefix.properties
@@ -0,0 +1,4 @@
+foo.bar = 1
+bar.baz = 2
+foo.bar.baz = 3
+foo.test = 4
\ No newline at end of file
diff --git a/idea/idea-completion/testData/basic/multifile/PropertyKeysWithPrefix/PropertyKeysWithPrefix.dependency.kt b/idea/idea-completion/testData/basic/multifile/PropertyKeysWithPrefix/PropertyKeysWithPrefix.dependency.kt
new file mode 100644
index 00000000000..1e1932b2d0f
--- /dev/null
+++ b/idea/idea-completion/testData/basic/multifile/PropertyKeysWithPrefix/PropertyKeysWithPrefix.dependency.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/idea-completion/testData/basic/multifile/PropertyKeysWithPrefix/PropertyKeysWithPrefix.kt b/idea/idea-completion/testData/basic/multifile/PropertyKeysWithPrefix/PropertyKeysWithPrefix.kt
new file mode 100644
index 00000000000..c6459771214
--- /dev/null
+++ b/idea/idea-completion/testData/basic/multifile/PropertyKeysWithPrefix/PropertyKeysWithPrefix.kt
@@ -0,0 +1,12 @@
+import org.jetbrains.annotations.PropertyKey
+
+fun message(@PropertyKey(resourceBundle = "PropertyKeysWithPrefix") key: String) = key
+
+fun test() {
+ message("foo.")
+}
+
+// EXIST: { lookupString: "foo.bar", itemText: "foo.bar", tailText: "1", typeText: "PropertyKeysWithPrefix" }
+// EXIST: { lookupString: "foo.bar.baz", itemText: "foo.bar.baz", tailText: "3", typeText: "PropertyKeysWithPrefix" }
+// EXIST: { lookupString: "foo.test", itemText: "foo.test", tailText: "4", typeText: "PropertyKeysWithPrefix" }
+// NOTHING_ELSE
\ No newline at end of file
diff --git a/idea/idea-completion/testData/basic/multifile/PropertyKeysWithPrefix/PropertyKeysWithPrefix.properties b/idea/idea-completion/testData/basic/multifile/PropertyKeysWithPrefix/PropertyKeysWithPrefix.properties
new file mode 100644
index 00000000000..5999617526a
--- /dev/null
+++ b/idea/idea-completion/testData/basic/multifile/PropertyKeysWithPrefix/PropertyKeysWithPrefix.properties
@@ -0,0 +1,4 @@
+foo.bar = 1
+bar.baz = 2
+foo.bar.baz = 3
+foo.test = 4
\ No newline at end of file
diff --git a/idea/idea-completion/testData/handlers/multifile/PropertyKeysWithPrefixEnter-1.kt b/idea/idea-completion/testData/handlers/multifile/PropertyKeysWithPrefixEnter-1.kt
new file mode 100644
index 00000000000..b0e268783e4
--- /dev/null
+++ b/idea/idea-completion/testData/handlers/multifile/PropertyKeysWithPrefixEnter-1.kt
@@ -0,0 +1,7 @@
+import org.jetbrains.annotations.PropertyKey
+
+fun message(@PropertyKey(resourceBundle = "TestBundle") key: String) = key
+
+fun test() {
+ message("foo.b")
+}
\ No newline at end of file
diff --git a/idea/idea-completion/testData/handlers/multifile/PropertyKeysWithPrefixEnter-2.kt b/idea/idea-completion/testData/handlers/multifile/PropertyKeysWithPrefixEnter-2.kt
new file mode 100644
index 00000000000..1e1932b2d0f
--- /dev/null
+++ b/idea/idea-completion/testData/handlers/multifile/PropertyKeysWithPrefixEnter-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/idea-completion/testData/handlers/multifile/PropertyKeysWithPrefixEnter.kt.after b/idea/idea-completion/testData/handlers/multifile/PropertyKeysWithPrefixEnter.kt.after
new file mode 100644
index 00000000000..d2bd1b98c64
--- /dev/null
+++ b/idea/idea-completion/testData/handlers/multifile/PropertyKeysWithPrefixEnter.kt.after
@@ -0,0 +1,7 @@
+import org.jetbrains.annotations.PropertyKey
+
+fun message(@PropertyKey(resourceBundle = "TestBundle") key: String) = key
+
+fun test() {
+ message("foo.barb")
+}
\ No newline at end of file
diff --git a/idea/idea-completion/testData/handlers/multifile/PropertyKeysWithPrefixTab-1.kt b/idea/idea-completion/testData/handlers/multifile/PropertyKeysWithPrefixTab-1.kt
new file mode 100644
index 00000000000..b0e268783e4
--- /dev/null
+++ b/idea/idea-completion/testData/handlers/multifile/PropertyKeysWithPrefixTab-1.kt
@@ -0,0 +1,7 @@
+import org.jetbrains.annotations.PropertyKey
+
+fun message(@PropertyKey(resourceBundle = "TestBundle") key: String) = key
+
+fun test() {
+ message("foo.b")
+}
\ No newline at end of file
diff --git a/idea/idea-completion/testData/handlers/multifile/PropertyKeysWithPrefixTab-2.kt b/idea/idea-completion/testData/handlers/multifile/PropertyKeysWithPrefixTab-2.kt
new file mode 100644
index 00000000000..1e1932b2d0f
--- /dev/null
+++ b/idea/idea-completion/testData/handlers/multifile/PropertyKeysWithPrefixTab-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/idea-completion/testData/handlers/multifile/PropertyKeysWithPrefixTab.kt.after b/idea/idea-completion/testData/handlers/multifile/PropertyKeysWithPrefixTab.kt.after
new file mode 100644
index 00000000000..98edf6d91e9
--- /dev/null
+++ b/idea/idea-completion/testData/handlers/multifile/PropertyKeysWithPrefixTab.kt.after
@@ -0,0 +1,7 @@
+import org.jetbrains.annotations.PropertyKey
+
+fun message(@PropertyKey(resourceBundle = "TestBundle") key: String) = key
+
+fun test() {
+ message("foo.bar")
+}
\ No newline at end of file
diff --git a/idea/idea-completion/testData/handlers/multifile/TestBundle.properties b/idea/idea-completion/testData/handlers/multifile/TestBundle.properties
new file mode 100644
index 00000000000..f3e552c26a7
--- /dev/null
+++ b/idea/idea-completion/testData/handlers/multifile/TestBundle.properties
@@ -0,0 +1,2 @@
+foo.bar = 1
+bar.baz = 2
\ No newline at end of file
diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/MultiFileJvmBasicCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/MultiFileJvmBasicCompletionTestGenerated.java
index abf17bb8a8b..5a3ff4c4b33 100644
--- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/MultiFileJvmBasicCompletionTestGenerated.java
+++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/MultiFileJvmBasicCompletionTestGenerated.java
@@ -293,6 +293,24 @@ public class MultiFileJvmBasicCompletionTestGenerated extends AbstractMultiFileJ
doTest(fileName);
}
+ @TestMetadata("PropertyKeysEmptyString")
+ public void testPropertyKeysEmptyString() throws Exception {
+ String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/PropertyKeysEmptyString/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("PropertyKeysNoPrefix")
+ public void testPropertyKeysNoPrefix() throws Exception {
+ String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/PropertyKeysNoPrefix/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("PropertyKeysWithPrefix")
+ public void testPropertyKeysWithPrefix() throws Exception {
+ String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/PropertyKeysWithPrefix/");
+ doTest(fileName);
+ }
+
@TestMetadata("SyntheticExtensionForGenericClass")
public void testSyntheticExtensionForGenericClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/SyntheticExtensionForGenericClass/");
diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/CompletionMultifileHandlerTest.kt b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/CompletionMultifileHandlerTest.kt
index 68c5546ba0b..dffa89898af 100644
--- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/CompletionMultifileHandlerTest.kt
+++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/CompletionMultifileHandlerTest.kt
@@ -77,9 +77,18 @@ class CompletionMultiFileHandlerTest : KotlinCompletionTestCase() {
doTest('!')
}
- fun doTest(completionChar: Char = '\n') {
+ fun testPropertyKeysWithPrefixEnter() {
+ doTest('\n', "TestBundle.properties")
+ }
+
+ fun testPropertyKeysWithPrefixTab() {
+ doTest('\t', "TestBundle.properties")
+ }
+
+ fun doTest(completionChar: Char = '\n', vararg extraFileNames: String) {
val fileName = getTestName(false)
+ configureByFiles(null, *extraFileNames)
configureByFiles(null, fileName + "-1.kt", fileName + "-2.kt")
complete(2)
if (myItems != null) {