Code completion for string templates after simple name + dot
This commit is contained in:
+1
@@ -54,6 +54,7 @@ import java.util.*
|
||||
class BasicCompletionSession(
|
||||
configuration: CompletionSessionConfiguration,
|
||||
parameters: CompletionParameters,
|
||||
toFromOriginalFileMapper: ToFromOriginalFileMapper,
|
||||
resultSet: CompletionResultSet
|
||||
) : CompletionSession(configuration, parameters, resultSet) {
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.intellij.codeInsight.completion.CompletionResultSet
|
||||
import com.intellij.codeInsight.completion.CompletionSorter
|
||||
import com.intellij.codeInsight.completion.CompletionUtil
|
||||
import com.intellij.codeInsight.completion.impl.CamelHumpMatcher
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.patterns.StandardPatterns
|
||||
import com.intellij.psi.search.DelegatingGlobalSearchScope
|
||||
@@ -161,9 +162,6 @@ abstract class CompletionSession(
|
||||
return KotlinIndicesHelper(resolutionFacade, searchScope, filter, filterOutPrivate = !mayIncludeInaccessible)
|
||||
}
|
||||
|
||||
protected val toFromOriginalFileMapper: ToFromOriginalFileMapper
|
||||
= ToFromOriginalFileMapper(parameters.originalFile as KtFile, position.containingFile as KtFile, parameters.offset)
|
||||
|
||||
// excludes top-level extensions except for ones declared in the current file - those that are fetched from indices
|
||||
protected val topLevelExtensionsExclude = object : DescriptorKindExclude() {
|
||||
val extensionsFromThisFile = file.declarations
|
||||
@@ -242,6 +240,10 @@ abstract class CompletionSession(
|
||||
return !collector.isResultEmpty
|
||||
}
|
||||
|
||||
public fun addLookupElementPostProcessor(processor: (LookupElement) -> LookupElement) {
|
||||
collector.addLookupElementPostProcessor(processor)
|
||||
}
|
||||
|
||||
protected abstract fun doComplete()
|
||||
|
||||
protected abstract val descriptorKindFilter: DescriptorKindFilter?
|
||||
|
||||
+91
-28
@@ -17,6 +17,8 @@
|
||||
package org.jetbrains.kotlin.idea.completion
|
||||
|
||||
import com.intellij.codeInsight.completion.*
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.codeInsight.lookup.LookupElementDecorator
|
||||
import com.intellij.openapi.editor.Document
|
||||
import com.intellij.openapi.progress.ProcessCanceledException
|
||||
import com.intellij.openapi.util.Key
|
||||
@@ -51,6 +53,8 @@ public class KotlinCompletionContributor : CompletionContributor() {
|
||||
|
||||
companion object {
|
||||
public val DEFAULT_DUMMY_IDENTIFIER: String = CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED + "$" // add '$' to ignore context after the caret
|
||||
|
||||
private val STRING_TEMPLATE_AFTER_DOT_REAL_START_OFFSET = OffsetKey.create("STRING_TEMPLATE_AFTER_DOT_REAL_START_OFFSET")
|
||||
}
|
||||
|
||||
init {
|
||||
@@ -67,10 +71,27 @@ public class KotlinCompletionContributor : CompletionContributor() {
|
||||
val psiFile = context.getFile()
|
||||
if (psiFile !is KtFile) return
|
||||
|
||||
// this code will make replacement offset "modified" and prevents altering it by the code in CompletionProgressIndicator
|
||||
context.replacementOffset = context.replacementOffset
|
||||
|
||||
val offset = context.getStartOffset()
|
||||
val tokenBefore = psiFile.findElementAt(Math.max(0, offset - 1))
|
||||
|
||||
val dummyIdentifier = when {
|
||||
if (offset > 0 && tokenBefore!!.node.elementType == KtTokens.REGULAR_STRING_PART && tokenBefore.text.startsWith(".")) {
|
||||
val prev = tokenBefore.parent.prevSibling
|
||||
if (prev != null && prev is KtSimpleNameStringTemplateEntry) {
|
||||
val expression = prev.expression
|
||||
if (expression != null) {
|
||||
val prefix = tokenBefore.text.substring(0, offset - tokenBefore.startOffset)
|
||||
context.dummyIdentifier = "{" + expression.text + prefix + CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED + "}"
|
||||
context.offsetMap.addOffset(CompletionInitializationContext.START_OFFSET, expression.startOffset)
|
||||
context.offsetMap.addOffset(STRING_TEMPLATE_AFTER_DOT_REAL_START_OFFSET, offset + 1)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.dummyIdentifier = when {
|
||||
context.getCompletionType() == CompletionType.SMART -> DEFAULT_DUMMY_IDENTIFIER
|
||||
|
||||
PackageDirectiveCompletion.ACTIVATION_PATTERN.accepts(tokenBefore) -> PackageDirectiveCompletion.DUMMY_IDENTIFIER
|
||||
@@ -87,10 +108,6 @@ public class KotlinCompletionContributor : CompletionContributor() {
|
||||
?: specialInArgumentListDummyIdentifier(tokenBefore)
|
||||
?: DEFAULT_DUMMY_IDENTIFIER
|
||||
}
|
||||
context.setDummyIdentifier(dummyIdentifier)
|
||||
|
||||
// this code will make replacement offset "modified" and prevents altering it by the code in CompletionProgressIndicator
|
||||
context.setReplacementOffset(context.getReplacementOffset())
|
||||
|
||||
if (context.getCompletionType() == CompletionType.SMART && !isAtEndOfLine(offset, context.getEditor().getDocument()) /* do not use parent expression if we are at the end of line - it's probably parsed incorrectly */) {
|
||||
val tokenAt = psiFile.findElementAt(Math.max(0, offset))
|
||||
@@ -216,17 +233,38 @@ public class KotlinCompletionContributor : CompletionContributor() {
|
||||
}
|
||||
|
||||
private fun performCompletion(parameters: CompletionParameters, result: CompletionResultSet) {
|
||||
val position = parameters.getPosition()
|
||||
val positionFile = position.containingFile
|
||||
if (positionFile !is KtFile) return
|
||||
if ((positionFile.originalFile as KtFile).doNotComplete ?: false) return
|
||||
val position = parameters.position
|
||||
val positionFile = position.containingFile as? KtFile ?: return
|
||||
val originalFile = parameters.originalFile as KtFile
|
||||
if (originalFile.doNotComplete ?: false) return
|
||||
|
||||
val toFromOriginalFileMapper = ToFromOriginalFileMapper(originalFile, positionFile, parameters.offset)
|
||||
|
||||
if (position.node.elementType == KtTokens.LONG_TEMPLATE_ENTRY_START) {
|
||||
val expression = (position.parent as KtBlockStringTemplateEntry).expression as KtDotQualifiedExpression
|
||||
val correctedPosition = (expression.selectorExpression as KtNameReferenceExpression).firstChild
|
||||
val context = position.getUserData(CompletionContext.COMPLETION_CONTEXT_KEY)!!
|
||||
val correctedOffset = context.offsetMap.getOffset(STRING_TEMPLATE_AFTER_DOT_REAL_START_OFFSET)
|
||||
val correctedParameters = parameters.withPosition(correctedPosition, correctedOffset)
|
||||
performCompletionWithOutOfBlockTracking(position) {
|
||||
doComplete(correctedParameters, toFromOriginalFileMapper, result,
|
||||
lookupElementPostProcessor = { wrapLookupElementForStringTemplateAfterDotCompletion(it) })
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
performCompletionWithOutOfBlockTracking(position) {
|
||||
doComplete(parameters, position, result)
|
||||
doComplete(parameters, toFromOriginalFileMapper, result)
|
||||
}
|
||||
}
|
||||
|
||||
private fun doComplete(parameters: CompletionParameters, position: PsiElement, result: CompletionResultSet) {
|
||||
private fun doComplete(
|
||||
parameters: CompletionParameters,
|
||||
toFromOriginalFileMapper: ToFromOriginalFileMapper,
|
||||
result: CompletionResultSet,
|
||||
lookupElementPostProcessor: ((LookupElement) -> LookupElement)? = null
|
||||
) {
|
||||
val position = parameters.position
|
||||
if (position.getNonStrictParentOfType<PsiComment>() != null) {
|
||||
// don't stop here, allow other contributors to run
|
||||
return
|
||||
@@ -244,12 +282,20 @@ public class KotlinCompletionContributor : CompletionContributor() {
|
||||
|
||||
if (PropertyKeyCompletion.perform(parameters, result)) return
|
||||
|
||||
fun addPostProcessor(session: CompletionSession) {
|
||||
if (lookupElementPostProcessor != null) {
|
||||
session.addLookupElementPostProcessor(lookupElementPostProcessor)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
result.restartCompletionWhenNothingMatches()
|
||||
|
||||
val configuration = CompletionSessionConfiguration(parameters)
|
||||
if (parameters.getCompletionType() == CompletionType.BASIC) {
|
||||
val session = BasicCompletionSession(configuration, parameters, result)
|
||||
val session = BasicCompletionSession(configuration, parameters, toFromOriginalFileMapper, result)
|
||||
|
||||
addPostProcessor(session)
|
||||
|
||||
if (parameters.isAutoPopup() && session.shouldDisableAutoPopup()) {
|
||||
result.stopHere()
|
||||
@@ -266,11 +312,16 @@ public class KotlinCompletionContributor : CompletionContributor() {
|
||||
completeJavaClassesNotToBeUsed = false,
|
||||
completeStaticMembers = parameters.invocationCount > 0
|
||||
)
|
||||
BasicCompletionSession(newConfiguration, parameters, result).complete()
|
||||
|
||||
val newSession = BasicCompletionSession(newConfiguration, parameters, toFromOriginalFileMapper, result)
|
||||
addPostProcessor(newSession)
|
||||
newSession.complete()
|
||||
}
|
||||
}
|
||||
else {
|
||||
SmartCompletionSession(configuration, parameters, result).complete()
|
||||
val session = SmartCompletionSession(configuration, parameters, toFromOriginalFileMapper, result)
|
||||
addPostProcessor(session)
|
||||
session.complete()
|
||||
}
|
||||
}
|
||||
catch (e: ProcessCanceledException) {
|
||||
@@ -278,6 +329,32 @@ public class KotlinCompletionContributor : CompletionContributor() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun wrapLookupElementForStringTemplateAfterDotCompletion(lookupElement: LookupElement): LookupElement {
|
||||
return object : LookupElementDecorator<LookupElement>(lookupElement) {
|
||||
override fun handleInsert(context: InsertionContext) {
|
||||
val document = context.document
|
||||
val startOffset = context.startOffset
|
||||
|
||||
val psiDocumentManager = PsiDocumentManager.getInstance(context.project)
|
||||
psiDocumentManager.commitAllDocuments()
|
||||
|
||||
assert(startOffset > 1 && document.charsSequence[startOffset - 1] == '.')
|
||||
val token = context.file.findElementAt(startOffset - 2)!!
|
||||
assert(token.node.elementType == KtTokens.IDENTIFIER)
|
||||
val nameRef = token.parent as KtNameReferenceExpression
|
||||
assert(nameRef.parent is KtSimpleNameStringTemplateEntry)
|
||||
|
||||
document.insertString(nameRef.startOffset, "{")
|
||||
|
||||
val tailOffset = context.tailOffset
|
||||
document.insertString(tailOffset, "}")
|
||||
context.tailOffset = tailOffset
|
||||
|
||||
super.handleInsert(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun shouldSuppressCompletion(parameters: CompletionParameters, prefixMatcher: PrefixMatcher): Boolean {
|
||||
val position = parameters.getPosition()
|
||||
val invocationCount = parameters.getInvocationCount()
|
||||
@@ -399,20 +476,6 @@ public class KotlinCompletionContributor : CompletionContributor() {
|
||||
return null
|
||||
}
|
||||
|
||||
private fun countParenthesisBalance(at: PsiElement, container: PsiElement): Int {
|
||||
val stopAt = container.prevLeaf()
|
||||
var current: PsiElement? = at
|
||||
var balance = 0
|
||||
while (current != stopAt) {
|
||||
when (current!!.getNode().getElementType()) {
|
||||
KtTokens.LPAR -> balance++
|
||||
KtTokens.RPAR -> balance--
|
||||
}
|
||||
current = current.prevLeaf()
|
||||
}
|
||||
return balance
|
||||
}
|
||||
|
||||
private fun isInUnclosedSuperQualifier(tokenBefore: PsiElement?): Boolean {
|
||||
if (tokenBefore == null) return false
|
||||
val tokensToSkip = TokenSet.orSet(TokenSet.create(KtTokens.IDENTIFIER, KtTokens.DOT ), KtTokens.WHITE_SPACE_OR_COMMENT_BIT_SET)
|
||||
|
||||
+6
-2
@@ -35,8 +35,12 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getCall
|
||||
import org.jetbrains.kotlin.resolve.calls.util.DelegatingCall
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
|
||||
class SmartCompletionSession(configuration: CompletionSessionConfiguration, parameters: CompletionParameters, resultSet: CompletionResultSet)
|
||||
: CompletionSession(configuration, parameters, resultSet) {
|
||||
class SmartCompletionSession(
|
||||
configuration: CompletionSessionConfiguration,
|
||||
parameters: CompletionParameters,
|
||||
toFromOriginalFileMapper: ToFromOriginalFileMapper,
|
||||
resultSet: CompletionResultSet
|
||||
) : CompletionSession(configuration, parameters, resultSet) {
|
||||
|
||||
override val descriptorKindFilter: DescriptorKindFilter by lazy {
|
||||
// we do not include SAM-constructors because they are handled separately and adding them requires iterating of java classes
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
fun foo(param: String) {
|
||||
val s = "$<caret>"
|
||||
val s = "$<caret>bla-bla-bla"
|
||||
}
|
||||
|
||||
// EXIST: foo
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fun foo(param: String) {
|
||||
val s = "$param.<caret>bla-bla-bla"
|
||||
}
|
||||
|
||||
// EXIST: { itemText: "length", attributes: "" }
|
||||
// EXIST: { itemText: "hashCode", attributes: "" }
|
||||
// EXIST: { itemText: "capitalize", attributes: "bold" }
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fun foo(param: String) {
|
||||
val s = "$param.l<caret>bla-bla-bla"
|
||||
}
|
||||
|
||||
// EXIST: { itemText: "length", attributes: "" }
|
||||
// ABSENT: hashCode
|
||||
// EXIST: { itemText: "lastIndex", attributes: "" }
|
||||
@@ -0,0 +1,6 @@
|
||||
fun foo(param: String) {
|
||||
val s = "$param.<caret>bla-bla-bla"
|
||||
}
|
||||
|
||||
// ELEMENT: equals
|
||||
// TAIL_TEXT: "(other: Any?)"
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
fun foo(param: String) {
|
||||
val s = "${param.equals(<caret>)}bla-bla-bla"
|
||||
}
|
||||
|
||||
// ELEMENT: equals
|
||||
// TAIL_TEXT: "(other: Any?)"
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(param: String) {
|
||||
val s = "$param.<caret>bla-bla-bla"
|
||||
}
|
||||
|
||||
// ELEMENT: hashCode
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
fun foo(param: String) {
|
||||
val s = "${param.hashCode()<caret>}bla-bla-bla"
|
||||
}
|
||||
|
||||
// ELEMENT: hashCode
|
||||
@@ -0,0 +1,6 @@
|
||||
fun foo(param: String) {
|
||||
val s = "$param.eq<caret>bla-bla-bla"
|
||||
}
|
||||
|
||||
// ELEMENT: equals
|
||||
// TAIL_TEXT: "(other: Any?)"
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
fun foo(param: String) {
|
||||
val s = "${param.equals(<caret>)}bla-bla-bla"
|
||||
}
|
||||
|
||||
// ELEMENT: equals
|
||||
// TAIL_TEXT: "(other: Any?)"
|
||||
+33
-12
@@ -673,18 +673,6 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("StringTemplate1.kt")
|
||||
public void testStringTemplate1() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/StringTemplate1.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("StringTemplate2.kt")
|
||||
public void testStringTemplate2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/StringTemplate2.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("SubpackageInFun.kt")
|
||||
public void testSubpackageInFun() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/SubpackageInFun.kt");
|
||||
@@ -1468,6 +1456,39 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/idea-completion/testData/basic/common/inStringLiterals")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class InStringLiterals extends AbstractJSBasicCompletionTest {
|
||||
public void testAllFilesPresentInInStringLiterals() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/inStringLiterals"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("StringTemplate1.kt")
|
||||
public void testStringTemplate1() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/inStringLiterals/StringTemplate1.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("StringTemplate2.kt")
|
||||
public void testStringTemplate2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/inStringLiterals/StringTemplate2.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("StringTemplateDot.kt")
|
||||
public void testStringTemplateDot() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/inStringLiterals/StringTemplateDot.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("StringTemplateDotSomething.kt")
|
||||
public void testStringTemplateDotSomething() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/inStringLiterals/StringTemplateDotSomething.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/idea-completion/testData/basic/common/lambdaSignature")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
+33
-12
@@ -673,18 +673,6 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("StringTemplate1.kt")
|
||||
public void testStringTemplate1() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/StringTemplate1.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("StringTemplate2.kt")
|
||||
public void testStringTemplate2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/StringTemplate2.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("SubpackageInFun.kt")
|
||||
public void testSubpackageInFun() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/SubpackageInFun.kt");
|
||||
@@ -1468,6 +1456,39 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/idea-completion/testData/basic/common/inStringLiterals")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class InStringLiterals extends AbstractJvmBasicCompletionTest {
|
||||
public void testAllFilesPresentInInStringLiterals() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/inStringLiterals"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("StringTemplate1.kt")
|
||||
public void testStringTemplate1() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/inStringLiterals/StringTemplate1.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("StringTemplate2.kt")
|
||||
public void testStringTemplate2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/inStringLiterals/StringTemplate2.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("StringTemplateDot.kt")
|
||||
public void testStringTemplateDot() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/inStringLiterals/StringTemplateDot.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("StringTemplateDotSomething.kt")
|
||||
public void testStringTemplateDotSomething() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/inStringLiterals/StringTemplateDotSomething.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/idea-completion/testData/basic/common/lambdaSignature")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
+18
@@ -591,6 +591,24 @@ public class BasicCompletionHandlerTestGenerated extends AbstractBasicCompletion
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("AfterDot1.kt")
|
||||
public void testAfterDot1() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/stringTemplate/AfterDot1.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("AfterDot2.kt")
|
||||
public void testAfterDot2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/stringTemplate/AfterDot2.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("AfterDot3.kt")
|
||||
public void testAfterDot3() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/stringTemplate/AfterDot3.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInStringTemplate() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/handlers/basic/stringTemplate"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.KtNodeTypes;
|
||||
import org.jetbrains.kotlin.lexer.KtTokens;
|
||||
import org.jetbrains.kotlin.psi.KtFile;
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameStringTemplateEntry;
|
||||
|
||||
public class KotlinTypedHandler extends TypedHandlerDelegate {
|
||||
private final static TokenSet CONTROL_FLOW_EXPRESSIONS = TokenSet.create(
|
||||
@@ -153,11 +154,18 @@ public class KotlinTypedHandler extends TypedHandlerDelegate {
|
||||
public boolean value(PsiFile file) {
|
||||
int offset = editor.getCaretModel().getOffset();
|
||||
|
||||
PsiElement lastElement = file.findElementAt(offset - 1);
|
||||
if (lastElement == null) return false;
|
||||
PsiElement lastToken = file.findElementAt(offset - 1);
|
||||
if (lastToken == null) return false;
|
||||
|
||||
IElementType elementType = lastElement.getNode().getElementType();
|
||||
return elementType == KtTokens.DOT || elementType == KtTokens.SAFE_ACCESS;
|
||||
IElementType elementType = lastToken.getNode().getElementType();
|
||||
if (elementType == KtTokens.DOT || elementType == KtTokens.SAFE_ACCESS) return true;
|
||||
|
||||
if (elementType == KtTokens.REGULAR_STRING_PART && lastToken.getTextRange().getStartOffset() == offset - 1) {
|
||||
PsiElement prevSibling = lastToken.getParent().getPrevSibling();
|
||||
return prevSibling != null && prevSibling instanceof KtSimpleNameStringTemplateEntry;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user