Resource Bundles: Enable property key completion in string literals

#KT-6946 Fixed
This commit is contained in:
Alexey Sedunov
2015-09-22 19:21:06 +03:00
parent 594e4e5c31
commit 6b9e2b0bbb
21 changed files with 191 additions and 1 deletions
+1
View File
@@ -23,5 +23,6 @@
<orderEntry type="module" module-name="util" />
<orderEntry type="module" module-name="idea-js" scope="TEST" />
<orderEntry type="module" module-name="js.frontend" />
<orderEntry type="library" scope="PROVIDED" name="properties" level="project" />
</component>
</module>
@@ -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<JetStringTemplateExpression>()?.isPlain() ?: false
}
}
@@ -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<JetStringTemplateExpression>() ?: return false
if (!template.isPlain()) return false
val references = template.references
val propertyReference = references.firstIsInstanceOrNull<PropertyReference>() ?: 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
}
}
@@ -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)
@@ -0,0 +1,13 @@
import org.jetbrains.annotations.PropertyKey
fun message(@PropertyKey(resourceBundle = "PropertyKeysEmptyString") key: String) = key
fun test() {
message("<caret>")
}
// 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
@@ -0,0 +1,4 @@
foo.bar = 1
bar.baz = 2
foo.bar.baz = 3
foo.test = 4
@@ -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)
@@ -0,0 +1,13 @@
import org.jetbrains.annotations.PropertyKey
fun message(@PropertyKey(resourceBundle = "PropertyKeysNoPrefix") key: String) = key
fun test() {
message("<caret>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
@@ -0,0 +1,4 @@
foo.bar = 1
bar.baz = 2
foo.bar.baz = 3
foo.test = 4
@@ -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)
@@ -0,0 +1,12 @@
import org.jetbrains.annotations.PropertyKey
fun message(@PropertyKey(resourceBundle = "PropertyKeysWithPrefix") key: String) = key
fun test() {
message("foo.<caret>")
}
// 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
@@ -0,0 +1,4 @@
foo.bar = 1
bar.baz = 2
foo.bar.baz = 3
foo.test = 4
@@ -0,0 +1,7 @@
import org.jetbrains.annotations.PropertyKey
fun message(@PropertyKey(resourceBundle = "TestBundle") key: String) = key
fun test() {
message("foo.<caret>b")
}
@@ -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)
@@ -0,0 +1,7 @@
import org.jetbrains.annotations.PropertyKey
fun message(@PropertyKey(resourceBundle = "TestBundle") key: String) = key
fun test() {
message("foo.barb")
}
@@ -0,0 +1,7 @@
import org.jetbrains.annotations.PropertyKey
fun message(@PropertyKey(resourceBundle = "TestBundle") key: String) = key
fun test() {
message("foo.<caret>b")
}
@@ -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)
@@ -0,0 +1,7 @@
import org.jetbrains.annotations.PropertyKey
fun message(@PropertyKey(resourceBundle = "TestBundle") key: String) = key
fun test() {
message("foo.bar")
}
@@ -0,0 +1,2 @@
foo.bar = 1
bar.baz = 2
@@ -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/");
@@ -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) {