Resource Bundles: Enable property key completion in string literals
#KT-6946 Fixed
This commit is contained in:
@@ -23,5 +23,6 @@
|
|||||||
<orderEntry type="module" module-name="util" />
|
<orderEntry type="module" module-name="util" />
|
||||||
<orderEntry type="module" module-name="idea-js" scope="TEST" />
|
<orderEntry type="module" module-name="idea-js" scope="TEST" />
|
||||||
<orderEntry type="module" module-name="js.frontend" />
|
<orderEntry type="module" module-name="js.frontend" />
|
||||||
|
<orderEntry type="library" scope="PROVIDED" name="properties" level="project" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
||||||
+8
@@ -79,6 +79,8 @@ public class KotlinCompletionContributor : CompletionContributor() {
|
|||||||
|
|
||||||
isInUnclosedSuperQualifier(tokenBefore) -> CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED + ">"
|
isInUnclosedSuperQualifier(tokenBefore) -> CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED + ">"
|
||||||
|
|
||||||
|
isInSimpleStringTemplate(tokenBefore) -> CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED
|
||||||
|
|
||||||
else -> specialLambdaSignatureDummyIdentifier(tokenBefore)
|
else -> specialLambdaSignatureDummyIdentifier(tokenBefore)
|
||||||
?: specialExtensionReceiverDummyIdentifier(tokenBefore)
|
?: specialExtensionReceiverDummyIdentifier(tokenBefore)
|
||||||
?: specialInTypeArgsDummyIdentifier(tokenBefore)
|
?: specialInTypeArgsDummyIdentifier(tokenBefore)
|
||||||
@@ -242,6 +244,8 @@ public class KotlinCompletionContributor : CompletionContributor() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (PropertyKeyCompletion.perform(parameters, result)) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
result.restartCompletionWhenNothingMatches()
|
result.restartCompletionWhenNothingMatches()
|
||||||
|
|
||||||
@@ -432,4 +436,8 @@ public class KotlinCompletionContributor : CompletionContributor() {
|
|||||||
val superToken = ltToken.prevLeaf { it !is PsiWhiteSpace && it !is PsiComment }
|
val superToken = ltToken.prevLeaf { it !is PsiWhiteSpace && it !is PsiComment }
|
||||||
return superToken?.node?.elementType == JetTokens.SUPER_KEYWORD
|
return superToken?.node?.elementType == JetTokens.SUPER_KEYWORD
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun isInSimpleStringTemplate(tokenBefore: PsiElement?): Boolean {
|
||||||
|
return tokenBefore?.parents?.firstIsInstanceOrNull<JetStringTemplateExpression>()?.isPlain() ?: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+49
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
@@ -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)
|
||||||
Vendored
+13
@@ -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
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
foo.bar = 1
|
||||||
|
bar.baz = 2
|
||||||
|
foo.bar.baz = 3
|
||||||
|
foo.test = 4
|
||||||
+5
@@ -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)
|
||||||
+13
@@ -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
|
||||||
Vendored
+4
@@ -0,0 +1,4 @@
|
|||||||
|
foo.bar = 1
|
||||||
|
bar.baz = 2
|
||||||
|
foo.bar.baz = 3
|
||||||
|
foo.test = 4
|
||||||
+5
@@ -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)
|
||||||
Vendored
+12
@@ -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
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
foo.bar = 1
|
||||||
|
bar.baz = 2
|
||||||
|
foo.bar.baz = 3
|
||||||
|
foo.test = 4
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
import org.jetbrains.annotations.PropertyKey
|
||||||
|
|
||||||
|
fun message(@PropertyKey(resourceBundle = "TestBundle") key: String) = key
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
message("foo.<caret>b")
|
||||||
|
}
|
||||||
+5
@@ -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)
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
import org.jetbrains.annotations.PropertyKey
|
||||||
|
|
||||||
|
fun message(@PropertyKey(resourceBundle = "TestBundle") key: String) = key
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
message("foo.barb")
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
import org.jetbrains.annotations.PropertyKey
|
||||||
|
|
||||||
|
fun message(@PropertyKey(resourceBundle = "TestBundle") key: String) = key
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
message("foo.<caret>b")
|
||||||
|
}
|
||||||
+5
@@ -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)
|
||||||
+7
@@ -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
|
||||||
+18
@@ -293,6 +293,24 @@ public class MultiFileJvmBasicCompletionTestGenerated extends AbstractMultiFileJ
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("SyntheticExtensionForGenericClass")
|
||||||
public void testSyntheticExtensionForGenericClass() throws Exception {
|
public void testSyntheticExtensionForGenericClass() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/SyntheticExtensionForGenericClass/");
|
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/SyntheticExtensionForGenericClass/");
|
||||||
|
|||||||
+10
-1
@@ -77,9 +77,18 @@ class CompletionMultiFileHandlerTest : KotlinCompletionTestCase() {
|
|||||||
doTest('!')
|
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)
|
val fileName = getTestName(false)
|
||||||
|
|
||||||
|
configureByFiles(null, *extraFileNames)
|
||||||
configureByFiles(null, fileName + "-1.kt", fileName + "-2.kt")
|
configureByFiles(null, fileName + "-1.kt", fileName + "-2.kt")
|
||||||
complete(2)
|
complete(2)
|
||||||
if (myItems != null) {
|
if (myItems != null) {
|
||||||
|
|||||||
Reference in New Issue
Block a user