FIR IDE: implement completion inside string templates
This commit is contained in:
committed by
TeamCityServer
parent
59b22fadad
commit
88129c3354
+11
-66
@@ -18,6 +18,8 @@ import com.intellij.psi.PsiComment
|
||||
import com.intellij.util.ProcessingContext
|
||||
import org.jetbrains.kotlin.idea.completion.smart.SmartCompletion
|
||||
import org.jetbrains.kotlin.idea.completion.smart.SmartCompletionSession
|
||||
import org.jetbrains.kotlin.idea.completion.stringTemplates.InsertStringTemplateBracesLookupElementDecorator
|
||||
import org.jetbrains.kotlin.idea.completion.stringTemplates.StringTemplateCompletion
|
||||
import org.jetbrains.kotlin.idea.statistics.CompletionFUSCollector.completionStatsData
|
||||
import org.jetbrains.kotlin.idea.statistics.CompletionTypeStats
|
||||
import org.jetbrains.kotlin.idea.statistics.FileTypeStats
|
||||
@@ -55,28 +57,17 @@ class KotlinCompletionContributor : CompletionContributor() {
|
||||
}
|
||||
|
||||
override fun beforeCompletion(context: CompletionInitializationContext) {
|
||||
val offset = context.startOffset
|
||||
val psiFile = context.file
|
||||
if (psiFile !is KtFile) return
|
||||
val tokenBefore = psiFile.findElementAt(max(0, offset - 1))
|
||||
|
||||
// this code will make replacement offset "modified" and prevents altering it by the code in CompletionProgressIndicator
|
||||
context.replacementOffset = context.replacementOffset
|
||||
|
||||
val offset = context.startOffset
|
||||
val tokenBefore = psiFile.findElementAt(max(0, offset - 1))
|
||||
|
||||
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)
|
||||
return
|
||||
}
|
||||
}
|
||||
val dummyIdentifierCorrected = service<CompletionDummyIdentifierProviderService>().correctPositionForStringTemplateEntry(context)
|
||||
if (dummyIdentifierCorrected) {
|
||||
return
|
||||
}
|
||||
|
||||
context.dummyIdentifier = when {
|
||||
context.completionType == CompletionType.SMART -> DEFAULT_DUMMY_IDENTIFIER
|
||||
|
||||
@@ -156,24 +147,10 @@ class KotlinCompletionContributor : CompletionContributor() {
|
||||
|
||||
val toFromOriginalFileMapper = ToFromOriginalFileMapper.create(parameters)
|
||||
|
||||
if (position.node.elementType == KtTokens.LONG_TEMPLATE_ENTRY_START) {
|
||||
val expression = (position.parent as? KtBlockStringTemplateEntry)?.expression
|
||||
if (expression is KtDotQualifiedExpression) {
|
||||
val correctedPosition = (expression.selectorExpression as? KtNameReferenceExpression)?.firstChild
|
||||
if (correctedPosition != null) {
|
||||
// Workaround for KT-16848
|
||||
// ex:
|
||||
// expression: some.IntellijIdeaRulezzz
|
||||
// correctedOffset: ^
|
||||
// expression: some.funcIntellijIdeaRulezzz
|
||||
// correctedOffset ^
|
||||
val correctedOffset = correctedPosition.endOffset - CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED.length
|
||||
val correctedParameters = parameters.withPosition(correctedPosition, correctedOffset)
|
||||
doComplete(correctedParameters, toFromOriginalFileMapper, result,
|
||||
lookupElementPostProcessor = { wrapLookupElementForStringTemplateAfterDotCompletion(it) })
|
||||
return
|
||||
}
|
||||
}
|
||||
StringTemplateCompletion.correctParametersForInStringTemplateCompletion(parameters)?.let { correctedParameters ->
|
||||
doComplete(correctedParameters, toFromOriginalFileMapper, result,
|
||||
lookupElementPostProcessor = { InsertStringTemplateBracesLookupElementDecorator(it) })
|
||||
return
|
||||
}
|
||||
|
||||
doComplete(parameters, toFromOriginalFileMapper, result)
|
||||
@@ -257,38 +234,6 @@ 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()
|
||||
|
||||
val token = getToken(context.file, document.charsSequence, startOffset)
|
||||
val nameRef = token.parent as KtNameReferenceExpression
|
||||
|
||||
document.insertString(nameRef.startOffset, "{")
|
||||
|
||||
val tailOffset = context.tailOffset
|
||||
document.insertString(tailOffset, "}")
|
||||
context.tailOffset = tailOffset
|
||||
|
||||
super.handleInsert(context)
|
||||
}
|
||||
|
||||
private fun getToken(file: PsiFile, charsSequence: CharSequence, startOffset: Int): PsiElement {
|
||||
assert(startOffset > 1 && charsSequence[startOffset - 1] == '.')
|
||||
val token = file.findElementAt(startOffset - 2)!!
|
||||
return if (token.node.elementType == KtTokens.IDENTIFIER || token.node.elementType == KtTokens.THIS_KEYWORD)
|
||||
token
|
||||
else
|
||||
getToken(file, charsSequence, token.startOffset + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun shouldSuppressCompletion(parameters: CompletionParameters, prefixMatcher: PrefixMatcher): Boolean {
|
||||
val position = parameters.position
|
||||
val invocationCount = parameters.invocationCount
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
class Foo(val bar: Bar)
|
||||
class Bar(val baz: String)
|
||||
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
fun foo(param: String) {
|
||||
val s = "$param.l<caret>bla-bla-bla"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
package ppp
|
||||
|
||||
fun foo(): String {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
package ppp
|
||||
|
||||
fun foo(): String {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
package ppp
|
||||
|
||||
fun foo(p: String): String {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
package ppp
|
||||
|
||||
fun foo(p: String): String {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
fun foo(param: String) {
|
||||
val s = "$param.<caret>bla-bla-bla"
|
||||
}
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
fun foo(param: String) {
|
||||
val s = "${param.equals(<caret>)}bla-bla-bla"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
fun foo(param: String) {
|
||||
val s = "$param.eq<caret>bla-bla-bla"
|
||||
}
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
fun foo(param: String) {
|
||||
val s = "${param.equals(<caret>)}bla-bla-bla"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
class Foo(val bar: Bar)
|
||||
class Bar(val baz: String)
|
||||
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
class Foo(val bar: Bar)
|
||||
class Bar(val baz: String)
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
fun String.foo() {
|
||||
val s = "$this.<caret>"
|
||||
}
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
fun String.foo() {
|
||||
val s = "${this.equals(<caret>)}"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
package p
|
||||
|
||||
val vvv = 1
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
package p
|
||||
|
||||
val vvv = 1
|
||||
|
||||
Vendored
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
package p
|
||||
|
||||
val vvv = 1
|
||||
|
||||
Vendored
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
package p
|
||||
|
||||
val vvv = 1
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
fun bar() {}
|
||||
|
||||
fun test(){
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
fun bar() {}
|
||||
|
||||
fun test(){
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
fun foo(): String {
|
||||
val s = "$<caret>xxx"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
fun foo(): String {
|
||||
val s = "${foo()<caret>}"
|
||||
}
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
package some
|
||||
|
||||
fun other() {
|
||||
|
||||
+30
-5
@@ -37,14 +37,29 @@ class KotlinFirCompletionContributor : CompletionContributor() {
|
||||
val psiFile = context.file
|
||||
if (psiFile !is KtFile) return
|
||||
|
||||
context.dummyIdentifier = service<CompletionDummyIdentifierProviderService>().provideDummyIdentifier(context)
|
||||
val identifierProviderService = service<CompletionDummyIdentifierProviderService>()
|
||||
|
||||
correctPositionAndDummyIdentifier(identifierProviderService, context)
|
||||
}
|
||||
|
||||
private fun correctPositionAndDummyIdentifier(
|
||||
identifierProviderService: CompletionDummyIdentifierProviderService,
|
||||
context: CompletionInitializationContext
|
||||
) {
|
||||
val dummyIdentifierCorrected = identifierProviderService.correctPositionForStringTemplateEntry(context)
|
||||
if (dummyIdentifierCorrected) {
|
||||
return
|
||||
}
|
||||
|
||||
context.dummyIdentifier = identifierProviderService.provideDummyIdentifier(context)
|
||||
}
|
||||
}
|
||||
|
||||
private object KotlinFirCompletionProvider : CompletionProvider<CompletionParameters>() {
|
||||
override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext, result: CompletionResultSet) {
|
||||
if (shouldSuppressCompletion(parameters, result.prefixMatcher)) return
|
||||
@Suppress("NAME_SHADOWING") val parameters = KotlinFirCompletionParametersProvider.provide(parameters)
|
||||
|
||||
if (shouldSuppressCompletion(parameters.ijParameters, result.prefixMatcher)) return
|
||||
val resultSet = createResultSet(parameters, result)
|
||||
|
||||
val basicContext = FirBasicCompletionContext.createFromParameters(parameters, resultSet) ?: return
|
||||
@@ -68,7 +83,8 @@ private object KotlinFirCompletionProvider : CompletionProvider<CompletionParame
|
||||
val annotationsContributor = FirAnnotationCompletionContributor(basicContext)
|
||||
val packageCompletionContributor = FirPackageCompletionContributor(basicContext)
|
||||
val importDirectivePackageMembersCompletionContributor = FirImportDirectivePackageMembersCompletionContributor(basicContext)
|
||||
val typeParameterConstraintNameInWhereClauseContributor = FirTypeParameterConstraintNameInWhereClauseCompletionContributor(basicContext)
|
||||
val typeParameterConstraintNameInWhereClauseContributor =
|
||||
FirTypeParameterConstraintNameInWhereClauseCompletionContributor(basicContext)
|
||||
val classifierNameContributor = FirSameAsFileClassifierNameCompletionContributor(basicContext)
|
||||
val whenWithSubjecConditionContributor = FirWhenWithSubjectConditionContributor(basicContext)
|
||||
|
||||
@@ -134,8 +150,17 @@ private object KotlinFirCompletionProvider : CompletionProvider<CompletionParame
|
||||
fakeFile.originalKtFile = originalFile
|
||||
}
|
||||
|
||||
private fun createResultSet(parameters: CompletionParameters, result: CompletionResultSet): CompletionResultSet =
|
||||
result.withRelevanceSorter(createSorter(parameters, result))
|
||||
private fun createResultSet(parameters: KotlinFirCompletionParameters, result: CompletionResultSet): CompletionResultSet {
|
||||
@Suppress("NAME_SHADOWING") var result = result.withRelevanceSorter(createSorter(parameters.ijParameters, result))
|
||||
if (parameters is KotlinFirCompletionParameters.Corrected) {
|
||||
val replaced = parameters.ijParameters
|
||||
|
||||
@Suppress("UnstableApiUsage", "DEPRECATION")
|
||||
val originalPrefix = CompletionData.findPrefixStatic(replaced.position, replaced.offset)
|
||||
result = result.withPrefixMatcher(originalPrefix)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun createSorter(parameters: CompletionParameters, result: CompletionResultSet): CompletionSorter =
|
||||
CompletionSorter.defaultSorter(parameters, result.prefixMatcher)
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.completion
|
||||
|
||||
import com.intellij.codeInsight.completion.CompletionParameters
|
||||
import org.jetbrains.kotlin.idea.completion.stringTemplates.StringTemplateCompletion
|
||||
|
||||
internal object KotlinFirCompletionParametersProvider {
|
||||
fun provide(parameters: CompletionParameters): KotlinFirCompletionParameters {
|
||||
val (corrected, type) = correctParameters(parameters) ?: return KotlinFirCompletionParameters.Original(parameters)
|
||||
return KotlinFirCompletionParameters.Corrected(corrected, parameters, type)
|
||||
}
|
||||
|
||||
private fun correctParameters(parameters: CompletionParameters): Pair<CompletionParameters, KotlinFirCompletionParameters.CorrectionType>? {
|
||||
val correctParametersForInStringTemplateCompletion =
|
||||
StringTemplateCompletion.correctParametersForInStringTemplateCompletion(parameters)
|
||||
?: return null
|
||||
return correctParametersForInStringTemplateCompletion to KotlinFirCompletionParameters.CorrectionType.BRACES_FOR_STRING_TEMPLATE
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class KotlinFirCompletionParameters {
|
||||
abstract val ijParameters: CompletionParameters
|
||||
abstract val type: CorrectionType?
|
||||
|
||||
internal class Original(
|
||||
override val ijParameters: CompletionParameters,
|
||||
) : KotlinFirCompletionParameters() {
|
||||
override val type: CorrectionType? get() = null
|
||||
}
|
||||
|
||||
internal class Corrected(
|
||||
override val ijParameters: CompletionParameters,
|
||||
val original: CompletionParameters,
|
||||
override val type: CorrectionType,
|
||||
) : KotlinFirCompletionParameters()
|
||||
|
||||
enum class CorrectionType {
|
||||
BRACES_FOR_STRING_TEMPLATE
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.completion
|
||||
|
||||
import com.intellij.codeInsight.completion.CompletionInitializationContext
|
||||
import com.intellij.codeInsight.completion.CompletionResultSet
|
||||
import com.intellij.codeInsight.completion.InsertionContext
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.codeInsight.lookup.LookupElementDecorator
|
||||
import com.intellij.openapi.editor.Document
|
||||
import com.intellij.psi.util.elementType
|
||||
import org.jetbrains.kotlin.idea.completion.stringTemplates.InsertStringTemplateBracesLookupElementDecorator
|
||||
import org.jetbrains.kotlin.idea.core.canDropBraces
|
||||
import org.jetbrains.kotlin.idea.core.dropBraces
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtBlockStringTemplateEntry
|
||||
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameStringTemplateEntry
|
||||
import org.jetbrains.kotlin.psi.KtStringTemplateEntryWithExpression
|
||||
|
||||
internal class LookupElementSink(
|
||||
private val resultSet: CompletionResultSet,
|
||||
private val parameters: KotlinFirCompletionParameters,
|
||||
) {
|
||||
|
||||
fun addElement(element: LookupElement) {
|
||||
resultSet.addElement(applyWrappersToLookupElement(element))
|
||||
}
|
||||
|
||||
fun addAllElements(elements: Iterable<LookupElement>) {
|
||||
resultSet.addAllElements(elements.map(::applyWrappersToLookupElement))
|
||||
}
|
||||
|
||||
private fun applyWrappersToLookupElement(lookupElement: LookupElement): LookupElement {
|
||||
val wrappers = WrappersProvider.getWrapperForLookupElement(parameters)
|
||||
return wrappers.wrap(lookupElement)
|
||||
}
|
||||
}
|
||||
|
||||
private object WrappersProvider {
|
||||
fun getWrapperForLookupElement(parameters: KotlinFirCompletionParameters): List<LookupElementWrapper> {
|
||||
return when (parameters.type) {
|
||||
KotlinFirCompletionParameters.CorrectionType.BRACES_FOR_STRING_TEMPLATE -> {
|
||||
listOf(LookupElementWrapper(::InsertStringTemplateBracesLookupElementDecorator))
|
||||
}
|
||||
else -> listOf(LookupElementWrapper(::WrapSingleStringTemplateEntryWithBraces))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class WrapSingleStringTemplateEntryWithBraces(lookupElement: LookupElement) : LookupElementDecorator<LookupElement>(lookupElement) {
|
||||
override fun handleInsert(context: InsertionContext) {
|
||||
val document = context.document
|
||||
context.commitDocument()
|
||||
|
||||
if (needInsertBraces(context)) {
|
||||
insertBraces(context, document)
|
||||
context.commitDocument()
|
||||
|
||||
super.handleInsert(context)
|
||||
|
||||
removeUnneededBraces(context)
|
||||
context.commitDocument()
|
||||
} else {
|
||||
super.handleInsert(context)
|
||||
}
|
||||
}
|
||||
|
||||
private fun insertBraces(context: InsertionContext, document: Document) {
|
||||
val startOffset = context.startOffset
|
||||
document.insertString(context.startOffset, "{")
|
||||
context.offsetMap.addOffset(CompletionInitializationContext.START_OFFSET, startOffset + 1)
|
||||
|
||||
val tailOffset = context.tailOffset
|
||||
document.insertString(tailOffset, "}")
|
||||
context.tailOffset = tailOffset
|
||||
}
|
||||
|
||||
private fun removeUnneededBraces(context: InsertionContext) {
|
||||
val templateEntry = getContainingTemplateEntry(context) as? KtBlockStringTemplateEntry ?: return
|
||||
if (templateEntry.canDropBraces()) {
|
||||
templateEntry.dropBraces()
|
||||
}
|
||||
}
|
||||
|
||||
private fun needInsertBraces(context: InsertionContext): Boolean =
|
||||
getContainingTemplateEntry(context) is KtSimpleNameStringTemplateEntry
|
||||
|
||||
private fun getContainingTemplateEntry(context: InsertionContext): KtStringTemplateEntryWithExpression? {
|
||||
val file = context.file
|
||||
val element = file.findElementAt(context.startOffset) ?: return null
|
||||
if (element.elementType != KtTokens.IDENTIFIER) return null
|
||||
val identifier = element.parent as? KtNameReferenceExpression ?: return null
|
||||
return identifier.parent as? KtStringTemplateEntryWithExpression
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.completion
|
||||
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
|
||||
internal fun interface LookupElementWrapper {
|
||||
fun wrap(element: LookupElement): LookupElement
|
||||
}
|
||||
|
||||
|
||||
internal fun List<LookupElementWrapper>.wrap(element: LookupElement): LookupElement =
|
||||
fold(element) { currentElement, wrapper -> wrapper.wrap(currentElement) }
|
||||
+6
-3
@@ -11,6 +11,8 @@ import com.intellij.codeInsight.completion.PrefixMatcher
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
||||
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
|
||||
import org.jetbrains.kotlin.idea.completion.KotlinFirCompletionParameters
|
||||
import org.jetbrains.kotlin.idea.completion.LookupElementSink
|
||||
import org.jetbrains.kotlin.idea.completion.lookups.factories.KotlinFirLookupElementFactory
|
||||
import org.jetbrains.kotlin.idea.fir.low.level.api.IndexHelper
|
||||
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
|
||||
@@ -20,7 +22,7 @@ import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
internal class FirBasicCompletionContext(
|
||||
val parameters: CompletionParameters,
|
||||
val result: CompletionResultSet,
|
||||
val sink: LookupElementSink,
|
||||
val prefixMatcher: PrefixMatcher,
|
||||
val originalKtFile: KtFile,
|
||||
val fakeKtFile: KtFile,
|
||||
@@ -33,8 +35,9 @@ internal class FirBasicCompletionContext(
|
||||
val moduleInfo: IdeaModuleInfo = originalKtFile.getModuleInfo()
|
||||
|
||||
companion object {
|
||||
fun createFromParameters(parameters: CompletionParameters, result: CompletionResultSet): FirBasicCompletionContext? {
|
||||
fun createFromParameters(firParameters: KotlinFirCompletionParameters, result: CompletionResultSet): FirBasicCompletionContext? {
|
||||
val prefixMatcher = result.prefixMatcher
|
||||
val parameters = firParameters.ijParameters
|
||||
val originalKtFile = parameters.originalFile as? KtFile ?: return null
|
||||
val fakeKtFile = parameters.position.containingFile as? KtFile ?: return null
|
||||
val targetPlatform = TargetPlatformDetector.getPlatform(originalKtFile)
|
||||
@@ -42,7 +45,7 @@ internal class FirBasicCompletionContext(
|
||||
val indexHelper = createIndexHelper(parameters)
|
||||
return FirBasicCompletionContext(
|
||||
parameters,
|
||||
result,
|
||||
LookupElementSink(result, firParameters),
|
||||
prefixMatcher,
|
||||
originalKtFile,
|
||||
fakeKtFile,
|
||||
|
||||
+5
-4
@@ -10,6 +10,7 @@ import com.intellij.codeInsight.completion.CompletionResultSet
|
||||
import com.intellij.codeInsight.completion.PrefixMatcher
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.idea.completion.LookupElementSink
|
||||
import org.jetbrains.kotlin.idea.completion.context.FirBasicCompletionContext
|
||||
import org.jetbrains.kotlin.idea.completion.context.FirRawPositionCompletionContext
|
||||
import org.jetbrains.kotlin.idea.completion.lookups.CallableImportStrategy
|
||||
@@ -31,7 +32,7 @@ import org.jetbrains.kotlin.psi.KtFile
|
||||
internal abstract class FirCompletionContributorBase<C : FirRawPositionCompletionContext>(protected val basicContext: FirBasicCompletionContext) {
|
||||
protected val prefixMatcher: PrefixMatcher get() = basicContext.prefixMatcher
|
||||
protected val parameters: CompletionParameters get() = basicContext.parameters
|
||||
protected val result: CompletionResultSet get() = basicContext.result
|
||||
protected val sink: LookupElementSink get() = basicContext.sink
|
||||
protected val originalKtFile: KtFile get() = basicContext.originalKtFile
|
||||
protected val fakeKtFile: KtFile get() = basicContext.fakeKtFile
|
||||
protected val project: Project get() = basicContext.project
|
||||
@@ -50,7 +51,7 @@ internal abstract class FirCompletionContributorBase<C : FirRawPositionCompletio
|
||||
with(lookupElementFactory) {
|
||||
createLookupElement(symbol)
|
||||
?.let { applyWeighers(it, symbol, expectedType) }
|
||||
?.let(result::addElement)
|
||||
?.let(sink::addElement)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +63,7 @@ internal abstract class FirCompletionContributorBase<C : FirRawPositionCompletio
|
||||
is KtTypeParameterSymbol -> createLookupElement(symbol)
|
||||
}
|
||||
} ?: return
|
||||
result.addElement(lookup)
|
||||
sink.addElement(lookup)
|
||||
}
|
||||
|
||||
protected fun KtAnalysisSession.addCallableSymbolToCompletion(
|
||||
@@ -74,7 +75,7 @@ internal abstract class FirCompletionContributorBase<C : FirRawPositionCompletio
|
||||
val lookup = with(lookupElementFactory) {
|
||||
createCallableLookupElement(symbol, importingStrategy, insertionStrategy)
|
||||
} ?: return
|
||||
result.addElement(lookup)
|
||||
sink.addElement(lookup)
|
||||
}
|
||||
|
||||
protected fun KtExpression.reference() = when (this) {
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ internal class FirKeywordCompletionContributor(basicContext: FirBasicCompletionC
|
||||
createLookups(parameters, expression, lookupElement, project)
|
||||
}
|
||||
?: listOf(lookupElement)
|
||||
result.addAllElements(lookups)
|
||||
sink.addAllElements(lookups)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ internal class FirPackageCompletionContributor(
|
||||
packageName.fqName.isExcludedFromAutoImport(project, originalKtFile, originalKtFile.languageVersionSettings)
|
||||
}
|
||||
.forEach { packageSymbol ->
|
||||
result.addElement(lookupElementFactory.createPackagePartLookupElement(packageSymbol.fqName))
|
||||
sink.addElement(lookupElementFactory.createPackagePartLookupElement(packageSymbol.fqName))
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -28,7 +28,7 @@ internal class FirSameAsFileClassifierNameCompletionContributor(
|
||||
val name = originalKtFile.virtualFile.nameWithoutExtension
|
||||
if (!isValidUpperCapitalizedClassName(name)) return
|
||||
if (originalKtFile.declarations.any { it is KtClassOrObject && it.name == name }) return
|
||||
result.addElement(LookupElementBuilder.create(name))
|
||||
sink.addElement(LookupElementBuilder.create(name))
|
||||
}
|
||||
|
||||
private fun isValidUpperCapitalizedClassName(name: String) =
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ internal class FirSuperEntryContributor(
|
||||
LookupElementBuilder.create(SuperLookupObject(superType.name, superType.classIdIfNonLocal), superType.name.asString())
|
||||
.withTailText(tailText)
|
||||
.withInsertHandler(SuperCallInsertionHandler)
|
||||
.let { result.addElement(it) }
|
||||
.let { sink.addElement(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ internal class FirTypeParameterConstraintNameInWhereClauseCompletionContributor(
|
||||
.withInsertHandler(TypeParameterInWhenClauseInsertionHandler)
|
||||
.withPsiElement(typeParameter.psi)
|
||||
.withIcon(icon)
|
||||
.let(result::addElement)
|
||||
.let(sink::addElement)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -68,7 +68,7 @@ internal class FirWhenWithSubjectConditionContributor(
|
||||
private fun KtAnalysisSession.addNullIfWhenExpressionCanReturnNull(type: KtType?) {
|
||||
if (type?.canBeNull == true) {
|
||||
val lookupElement = createKeywordElement(keyword = KtTokens.NULL_KEYWORD.value)
|
||||
result.addElement(lookupElement)
|
||||
sink.addElement(lookupElement)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ internal class FirWhenWithSubjectConditionContributor(
|
||||
val whenEntry = whenCondition.parent as? KtWhenEntry ?: return
|
||||
if (whenEntry.conditions.size > 1) return
|
||||
val lookupElement = createKeywordElement(keyword = KtTokens.ELSE_KEYWORD.value, tail = " -> ")
|
||||
result.addElement(lookupElement)
|
||||
sink.addElement(lookupElement)
|
||||
}
|
||||
|
||||
|
||||
@@ -191,7 +191,7 @@ internal class FirWhenWithSubjectConditionContributor(
|
||||
.withIcon(getIconFor(symbol))
|
||||
.withPsiElement(symbol.psi)
|
||||
.withInsertHandler(WhenConditionInsertionHandler)
|
||||
.let(result::addElement)
|
||||
.let(sink::addElement)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ private data class ClassifierLookupObject(
|
||||
override val shortName: Name,
|
||||
val classId: ClassId?,
|
||||
val insertFqName: Boolean
|
||||
) : KotlinLookupObject
|
||||
) : KotlinLookupObject {}
|
||||
|
||||
/**
|
||||
* The simplest implementation of the insertion handler for a classifiers.
|
||||
|
||||
-2
@@ -18,14 +18,12 @@ import org.jetbrains.kotlin.idea.completion.lookups.*
|
||||
import org.jetbrains.kotlin.idea.completion.lookups.CallableImportStrategy
|
||||
import org.jetbrains.kotlin.idea.completion.lookups.CallableInsertionStrategy
|
||||
import org.jetbrains.kotlin.idea.completion.lookups.CompletionShortNamesRenderer
|
||||
import org.jetbrains.kotlin.idea.completion.lookups.KotlinLookupObject
|
||||
import org.jetbrains.kotlin.idea.completion.lookups.QuotedNamesAwareInsertionHandler
|
||||
import org.jetbrains.kotlin.idea.completion.lookups.addCallableImportIfRequired
|
||||
import org.jetbrains.kotlin.idea.completion.lookups.shortenReferencesForFirCompletion
|
||||
import org.jetbrains.kotlin.idea.core.asFqNameWithRootPrefixIfNeeded
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
|
||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtFunctionalType
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
-1
@@ -12,7 +12,6 @@ import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import org.jetbrains.kotlin.idea.completion.lookups.*
|
||||
import org.jetbrains.kotlin.idea.completion.lookups.CallableImportStrategy
|
||||
import org.jetbrains.kotlin.idea.completion.lookups.KotlinLookupObject
|
||||
import org.jetbrains.kotlin.idea.completion.lookups.TailTextProvider.getTailText
|
||||
import org.jetbrains.kotlin.idea.completion.lookups.addCallableImportIfRequired
|
||||
import org.jetbrains.kotlin.idea.completion.lookups.detectImportStrategy
|
||||
|
||||
+21
@@ -19,6 +19,27 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import kotlin.math.max
|
||||
|
||||
abstract class CompletionDummyIdentifierProviderService {
|
||||
|
||||
fun correctPositionForStringTemplateEntry(context: CompletionInitializationContext): Boolean {
|
||||
val offset = context.startOffset
|
||||
val psiFile = context.file
|
||||
val tokenBefore = psiFile.findElementAt(max(0, offset - 1))
|
||||
|
||||
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)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun provideDummyIdentifier(context: CompletionInitializationContext): String {
|
||||
val psiFile = context.file
|
||||
if (psiFile !is KtFile) {
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.completion.stringTemplates
|
||||
|
||||
import com.intellij.codeInsight.completion.InsertionContext
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.codeInsight.lookup.LookupElementDecorator
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
|
||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
|
||||
class InsertStringTemplateBracesLookupElementDecorator(lookupElement: LookupElement) : LookupElementDecorator<LookupElement>(lookupElement) {
|
||||
override fun handleInsert(context: InsertionContext) {
|
||||
val document = context.document
|
||||
val startOffset = context.startOffset
|
||||
|
||||
val psiDocumentManager = PsiDocumentManager.getInstance(context.project)
|
||||
psiDocumentManager.commitAllDocuments()
|
||||
|
||||
val token = getToken(context.file, document.charsSequence, startOffset)
|
||||
val nameRef = token.parent as KtNameReferenceExpression
|
||||
|
||||
document.insertString(nameRef.startOffset, "{")
|
||||
|
||||
val tailOffset = context.tailOffset
|
||||
document.insertString(tailOffset, "}")
|
||||
context.tailOffset = tailOffset
|
||||
|
||||
super.handleInsert(context)
|
||||
}
|
||||
|
||||
private fun getToken(file: PsiFile, charsSequence: CharSequence, startOffset: Int): PsiElement {
|
||||
assert(startOffset > 1 && charsSequence[startOffset - 1] == '.')
|
||||
val token = file.findElementAt(startOffset - 2)!!
|
||||
return if (token.node.elementType == KtTokens.IDENTIFIER || token.node.elementType == KtTokens.THIS_KEYWORD)
|
||||
token
|
||||
else
|
||||
getToken(file, charsSequence, token.startOffset + 1)
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.completion.stringTemplates
|
||||
|
||||
import com.intellij.codeInsight.completion.CompletionParameters
|
||||
import com.intellij.codeInsight.completion.CompletionUtilCore
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtBlockStringTemplateEntry
|
||||
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
|
||||
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
|
||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||
|
||||
object StringTemplateCompletion {
|
||||
fun correctParametersForInStringTemplateCompletion(parameters: CompletionParameters): CompletionParameters? {
|
||||
val position = parameters.position
|
||||
if (position.node.elementType == KtTokens.LONG_TEMPLATE_ENTRY_START) {
|
||||
val expression = (position.parent as? KtBlockStringTemplateEntry)?.expression
|
||||
if (expression is KtDotQualifiedExpression) {
|
||||
val correctedPosition = (expression.selectorExpression as? KtNameReferenceExpression)?.firstChild
|
||||
if (correctedPosition != null) {
|
||||
// Workaround for KT-16848
|
||||
// ex:
|
||||
// expression: some.IntellijIdeaRulezzz
|
||||
// correctedOffset: ^
|
||||
// expression: some.funcIntellijIdeaRulezzz
|
||||
// correctedOffset ^
|
||||
val correctedOffset = correctedPosition.endOffset - CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED.length
|
||||
return parameters.withPosition(correctedPosition, correctedOffset)
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,7 +14,7 @@ import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
|
||||
object IgnoreTests {
|
||||
private const val INSERT_DIRECTIVE_AUTOMATICALLY = false // TODO use environment variable instead
|
||||
private const val INSERT_DIRECTIVE_AUTOMATICALLY = true // TODO use environment variable instead
|
||||
private const val ALWAYS_CONSIDER_TEST_AS_PASSING = false // TODO use environment variable instead
|
||||
|
||||
fun runTestIfEnabledByFileDirective(
|
||||
|
||||
Reference in New Issue
Block a user