Code Insight: Support file path references inside of Kotlin string literals

#KT-11704 Fixed
This commit is contained in:
Alexey Sedunov
2016-04-11 19:42:13 +03:00
parent 6a309bdb4e
commit 4a65b1c65c
26 changed files with 128 additions and 21 deletions
+1
View File
@@ -22,6 +22,7 @@ New features:
- Enable injection from Java or Kotlin function declaration by annotating parameter with @Language annotation
- [KT-11807](https://youtrack.jetbrains.com/issue/KT-11807) Use function body template when generating overriding functions with default body
- [KT-12079](https://youtrack.jetbrains.com/issue/KT-12079) Support "Autowired members defined in invalid Spring bean" inspection on Kotlin declarations
- [KT-11704](https://youtrack.jetbrains.com/issue/KT-11704) Support file path references inside of Kotlin string literals
Issues fixed:
@@ -0,0 +1,6 @@
// NUMBER: 1
// EXIST: foo.txt
fun foo() {
val s = "fo<caret>"
}
@@ -0,0 +1,8 @@
// NUMBER: 3
// EXIST: foo.txt
// EXIST: bar.txt
// EXIST: FileRefInStringLiteralNoPrefix.kt
fun foo() {
val s = "<caret>"
}
@@ -0,0 +1,3 @@
fun foo() {
val s = "fo<caret>something"
}
@@ -0,0 +1 @@
// Empty file to satisfy test framework requirements
@@ -0,0 +1,3 @@
fun foo() {
val s = "foo.txt"
}
@@ -0,0 +1,3 @@
fun foo() {
val s = "fo<caret>something"
}
@@ -0,0 +1 @@
// Empty file to satisfy test framework requirements
@@ -0,0 +1,3 @@
fun foo() {
val s = "foo.txt"
}
@@ -125,6 +125,18 @@ public class MultiFileJvmBasicCompletionTestGenerated extends AbstractMultiFileJ
doTest(fileName);
}
@TestMetadata("FileRefInStringLiteral")
public void testFileRefInStringLiteral() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/FileRefInStringLiteral/");
doTest(fileName);
}
@TestMetadata("FileRefInStringLiteralNoPrefix")
public void testFileRefInStringLiteralNoPrefix() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/FileRefInStringLiteralNoPrefix/");
doTest(fileName);
}
@TestMetadata("GroovyClassNameCompletionFromDefaultPackage")
public void testGroovyClassNameCompletionFromDefaultPackage() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/GroovyClassNameCompletionFromDefaultPackage/");
@@ -85,6 +85,14 @@ class CompletionMultiFileHandlerTest : KotlinCompletionTestCase() {
doTest('\t', "TestBundle.properties")
}
fun testFileRefInStringLiteralEnter() {
doTest('\n', "foo.txt", "bar.txt")
}
fun testFileRefInStringLiteralTab() {
doTest('\t', "foo.txt", "bar.txt")
}
fun testNotImportedExtension() {
doTest()
}
+1
View File
@@ -395,6 +395,7 @@
<weigher key="completion" implementationClass="org.jetbrains.kotlin.idea.completion.KotlinLookupElementProximityWeigher" id="kotlin.proximity" order="after proximity"/>
<psi.referenceContributor language="kotlin" implementation="org.jetbrains.kotlin.idea.references.KotlinReferenceContributor"/>
<psi.referenceContributor language="kotlin" implementation="org.jetbrains.kotlin.idea.references.KotlinFilePathReferenceContributor"/>
<renamePsiElementProcessor id="KotlinClass"
implementation="org.jetbrains.kotlin.idea.refactoring.rename.RenameKotlinClassProcessor"
@@ -22,12 +22,17 @@ import org.jetbrains.kotlin.KtNodeTypes.*
import org.jetbrains.kotlin.psi.KtContainerNode
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.kdoc.parser.KDocElementTypes
import org.jetbrains.kotlin.lexer.KtTokens
class KotlinWordSelectionFilter : Condition<PsiElement>{
override fun value(e: PsiElement): Boolean {
if (e.language != KotlinLanguage.INSTANCE) return true
if (KotlinListSelectioner.canSelect(e)) return false
val elementType = e.node.elementType
if (elementType == KtTokens.REGULAR_STRING_PART || elementType == KtTokens.ESCAPE_SEQUENCE) return true
if (e is KtContainerNode) return false
if (e.parent.firstChild.nextSibling == null && e.parent !is KtContainerNode) return false // skip nodes with the same range as their parent
@@ -0,0 +1,45 @@
/*
* Copyright 2010-2016 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.references
import com.intellij.patterns.PlatformPatterns
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReference
import com.intellij.psi.PsiReferenceRegistrar
import com.intellij.psi.impl.source.resolve.reference.impl.providers.FilePathReferenceProvider
import com.intellij.util.ProcessingContext
import org.jetbrains.kotlin.psi.KtStringTemplateExpression
import org.jetbrains.kotlin.psi.psiUtil.getContentRange
import org.jetbrains.kotlin.psi.psiUtil.isPlainWithEscapes
import org.jetbrains.kotlin.psi.psiUtil.plainContent
class KotlinFilePathReferenceContributor : AbstractKotlinReferenceContributor() {
object KotlinFilePathReferenceProvider : FilePathReferenceProvider() {
override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array<out PsiReference> {
if (element !is KtStringTemplateExpression) return PsiReference.EMPTY_ARRAY
if (!element.isPlainWithEscapes()) return PsiReference.EMPTY_ARRAY
return getReferencesByElement(element, element.plainContent, element.getContentRange().startOffset, true)
}
}
override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) {
registrar.registerReferenceProvider(
PlatformPatterns.psiElement(KtStringTemplateExpression::class.java),
KotlinFilePathReferenceProvider
)
}
}
@@ -0,0 +1,3 @@
class Foo {
}
@@ -0,0 +1,5 @@
fun foo() {
val s = """idea/testData/resolve/references/<caret>fileRefInRawStringLiteral.Data.java"""
}
// REF: /src/<test dir>/resolve/references.fileRefInRawStringLiteral.Data.java
@@ -0,0 +1,3 @@
class Foo {
}
@@ -0,0 +1,5 @@
fun foo() {
val s = "idea/testData/resolve/references/<caret>fileRefInStringLiteral.Data.java"
}
// REF: /src/<test dir>/resolve/references.fileRefInStringLiteral.Data.java
@@ -179,6 +179,18 @@ public class ReferenceResolveTestGenerated extends AbstractReferenceResolveTest
doTest(fileName);
}
@TestMetadata("fileRefInRawStringLiteral.kt")
public void testFileRefInRawStringLiteral() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/resolve/references/fileRefInRawStringLiteral.kt");
doTest(fileName);
}
@TestMetadata("fileRefInStringLiteral.kt")
public void testFileRefInStringLiteral() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/resolve/references/fileRefInStringLiteral.kt");
doTest(fileName);
}
@TestMetadata("GenericFunctionParameter.kt")
public void testGenericFunctionParameter() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/resolve/references/GenericFunctionParameter.kt");
@@ -17,33 +17,12 @@
package org.jetbrains.kotlin.psi
import com.intellij.lang.html.HTMLLanguage
import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReference
import junit.framework.TestCase
import org.intellij.lang.regexp.RegExpLanguage
import org.intellij.plugins.intelliLang.Configuration
import org.intellij.plugins.intelliLang.inject.InjectLanguageAction
import org.intellij.plugins.intelliLang.inject.UnInjectLanguageAction
import org.intellij.plugins.intelliLang.inject.config.BaseInjection
import org.intellij.plugins.intelliLang.inject.config.InjectionPlace
import org.intellij.plugins.intelliLang.references.FileReferenceInjector
class KotlinInjectionTest : AbstractInjectionTest() {
fun testInjectUnInjectOnSimpleString() {
myFixture.configureByText("test.kt",
"""val test = "<caret>simple" """)
TestCase.assertTrue(InjectLanguageAction().isAvailable(project, myFixture.editor, myFixture.file))
TestCase.assertFalse(UnInjectLanguageAction().isAvailable(project, myFixture.editor, myFixture.file))
InjectLanguageAction.invokeImpl(project, myFixture.editor, myFixture.file, FileReferenceInjector())
TestCase.assertTrue(myFixture.getReferenceAtCaretPosition() is FileReference)
TestCase.assertFalse(InjectLanguageAction().isAvailable(project, myFixture.editor, myFixture.file))
TestCase.assertTrue(UnInjectLanguageAction().isAvailable(project, myFixture.editor, myFixture.file))
UnInjectLanguageAction.invokeImpl(project, myFixture.editor, myFixture.file)
TestCase.assertNull(myFixture.getReferenceAtCaretPosition())
}
fun testInjectionOnJavaPredefinedMethodWithAnnotation() = doInjectionPresentTest(
"""
val test1 = java.util.regex.Pattern.compile("<caret>pattern")