FIR IDE: reuse keyword completion handlers which do not require resovle
This commit is contained in:
committed by
TeamCityServer
parent
1f93eb0841
commit
36068d0b94
+7
-43
@@ -20,12 +20,10 @@ import com.intellij.psi.*
|
||||
import com.intellij.psi.impl.source.tree.LeafPsiElement
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.util.ProcessingContext
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.util.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.codeInsight.ReferenceVariantsHelper
|
||||
import org.jetbrains.kotlin.idea.completion.handlers.createKeywordConstructLookupElement
|
||||
import org.jetbrains.kotlin.idea.completion.smart.ExpectedInfoMatch
|
||||
import org.jetbrains.kotlin.idea.completion.smart.SMART_COMPLETION_ITEM_PRIORITY_KEY
|
||||
import org.jetbrains.kotlin.idea.completion.smart.SmartCompletion
|
||||
@@ -581,11 +579,17 @@ class BasicCompletionSession(
|
||||
)
|
||||
|
||||
val keywordsPrefix = prefix.substringBefore('@') // if there is '@' in the prefix - use shorter prefix to not loose 'this' etc
|
||||
val isUseSiteAnnotationTarget = position.prevLeaf()?.node?.elementType == KtTokens.AT
|
||||
keywordCompletion.complete(expression ?: parameters.position, resultSet.prefixMatcher, isJvmModule) { lookupElement ->
|
||||
val keyword = lookupElement.lookupString
|
||||
if (keyword in keywordsToSkip) return@complete
|
||||
|
||||
val completionKeywordHandler = DefaultCompletionKeywordHandlers.defaultHandlers.getHandlerForKeyword(keyword)
|
||||
if (completionKeywordHandler != null) {
|
||||
val lookups = completionKeywordHandler.createLookups(parameters, expression, lookupElement, project)
|
||||
collector.addElements(lookups)
|
||||
return@complete
|
||||
}
|
||||
|
||||
when (keyword) {
|
||||
// if "this" is parsed correctly in the current context - insert it and all this@xxx items
|
||||
"this" -> {
|
||||
@@ -610,12 +614,6 @@ class BasicCompletionSession(
|
||||
}
|
||||
}
|
||||
|
||||
"break", "continue" -> {
|
||||
if (expression != null) {
|
||||
collector.addElements(breakOrContinueExpressionItems(expression, keyword))
|
||||
}
|
||||
}
|
||||
|
||||
"override" -> {
|
||||
collector.addElement(lookupElement)
|
||||
|
||||
@@ -628,40 +626,6 @@ class BasicCompletionSession(
|
||||
}
|
||||
}
|
||||
|
||||
"get" -> {
|
||||
collector.addElement(lookupElement)
|
||||
|
||||
if (!isUseSiteAnnotationTarget) {
|
||||
collector.addElement(createKeywordConstructLookupElement(project, keyword, "val v:Int get()=caret"))
|
||||
collector.addElement(
|
||||
createKeywordConstructLookupElement(
|
||||
project,
|
||||
keyword,
|
||||
"val v:Int get(){caret}",
|
||||
trimSpacesAroundCaret = true
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
"set" -> {
|
||||
collector.addElement(lookupElement)
|
||||
|
||||
if (!isUseSiteAnnotationTarget) {
|
||||
collector.addElement(createKeywordConstructLookupElement(project, keyword, "var v:Int set(value)=caret"))
|
||||
collector.addElement(
|
||||
createKeywordConstructLookupElement(
|
||||
project,
|
||||
keyword,
|
||||
"var v:Int set(value){caret}",
|
||||
trimSpacesAroundCaret = true
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
"contract" -> { }
|
||||
|
||||
else -> collector.addElement(lookupElement)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,7 +304,6 @@ private fun KtDeclarationWithBody.returnType(bindingContext: BindingContext): Ko
|
||||
return callable.returnType
|
||||
}
|
||||
|
||||
private fun Name?.labelNameToTail(): String = if (this != null) "@" + render() else ""
|
||||
|
||||
private fun createKeywordElementWithSpace(
|
||||
keyword: String,
|
||||
@@ -324,42 +323,7 @@ private fun createKeywordElementWithSpace(
|
||||
}
|
||||
}
|
||||
|
||||
private fun createKeywordElement(
|
||||
keyword: String,
|
||||
tail: String = "",
|
||||
lookupObject: KeywordLookupObject = KeywordLookupObject()
|
||||
): LookupElementBuilder {
|
||||
var element = LookupElementBuilder.create(lookupObject, keyword + tail)
|
||||
element = element.withPresentableText(keyword)
|
||||
element = element.withBoldness(true)
|
||||
if (tail.isNotEmpty()) {
|
||||
element = element.withTailText(tail, false)
|
||||
}
|
||||
return element
|
||||
}
|
||||
|
||||
fun breakOrContinueExpressionItems(position: KtElement, breakOrContinue: String): Collection<LookupElement> {
|
||||
val result = ArrayList<LookupElement>()
|
||||
|
||||
parentsLoop@
|
||||
for (parent in position.parentsWithSelf) {
|
||||
when (parent) {
|
||||
is KtLoopExpression -> {
|
||||
if (result.isEmpty()) {
|
||||
result.add(createKeywordElement(breakOrContinue))
|
||||
}
|
||||
|
||||
val label = (parent.parent as? KtLabeledExpression)?.getLabelNameAsName()
|
||||
if (label != null) {
|
||||
result.add(createKeywordElement(breakOrContinue, tail = label.labelNameToTail()))
|
||||
}
|
||||
}
|
||||
|
||||
is KtDeclarationWithBody -> break@parentsLoop //TODO: support non-local break's&continue's when they are supported by compiler
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun BasicLookupElementFactory.createLookupElementForType(type: KotlinType): LookupElement? {
|
||||
if (type.isError) return null
|
||||
|
||||
@@ -5,11 +5,17 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.completion
|
||||
|
||||
import com.intellij.codeInsight.completion.CompletionParameters
|
||||
import com.intellij.codeInsight.completion.CompletionResultSet
|
||||
import com.intellij.codeInsight.completion.PrefixMatcher
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.psi.KtExpressionWithLabel
|
||||
import org.jetbrains.kotlin.psi.KtLabelReferenceExpression
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
|
||||
object FirKeywordCompletion {
|
||||
private val keywordCompletion = KeywordCompletion(object : KeywordCompletion.LanguageVersionSettingProvider {
|
||||
@@ -17,9 +23,29 @@ object FirKeywordCompletion {
|
||||
override fun getLanguageVersionSetting(module: Module) = LanguageVersionSettingsImpl.DEFAULT // TODO
|
||||
})
|
||||
|
||||
fun completeKeywords(result: CompletionResultSet, position: PsiElement, prefixMatcher: PrefixMatcher, isJvmModule: Boolean) {
|
||||
keywordCompletion.complete(position, prefixMatcher, isJvmModule) { lookupElement ->
|
||||
result.addElement(lookupElement)
|
||||
fun completeKeywords(
|
||||
result: CompletionResultSet,
|
||||
parameters: CompletionParameters,
|
||||
position: PsiElement,
|
||||
prefixMatcher: PrefixMatcher,
|
||||
project: Project,
|
||||
isJvmModule: Boolean
|
||||
) {
|
||||
val reference = (position.parent as? KtSimpleNameExpression)?.mainReference
|
||||
val expression = if (reference != null) { // logic is copy-n-pasted from CompletionSession.expression
|
||||
if (reference.expression is KtLabelReferenceExpression) {
|
||||
reference.expression.parent.parent as? KtExpressionWithLabel
|
||||
} else {
|
||||
reference.expression
|
||||
}
|
||||
} else null
|
||||
keywordCompletion.complete(expression ?: position, prefixMatcher, isJvmModule) { lookupElement ->
|
||||
val keyword = lookupElement.lookupString
|
||||
val completionKeywordHandler = DefaultCompletionKeywordHandlers.defaultHandlers.getHandlerForKeyword(keyword)
|
||||
val lookups = completionKeywordHandler
|
||||
?.createLookups(parameters, expression, lookupElement, project)
|
||||
?: listOf(lookupElement)
|
||||
result.addAllElements(lookups)
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -143,10 +143,11 @@ private class KotlinCommonCompletionProvider(
|
||||
fun addCompletions(parameters: CompletionParameters, result: CompletionResultSet) {
|
||||
val originalFile = parameters.originalFile as? KtFile ?: return
|
||||
val isJvmModule = TargetPlatformDetector.getPlatform(originalFile).isJvm()
|
||||
val project = originalFile.project
|
||||
|
||||
recordOriginalFile(parameters)
|
||||
|
||||
FirKeywordCompletion.completeKeywords(result, parameters.position, prefixMatcher, isJvmModule = isJvmModule)
|
||||
FirKeywordCompletion.completeKeywords(result, parameters, parameters.position, prefixMatcher, project, isJvmModule = isJvmModule)
|
||||
|
||||
val reference = (parameters.position.parent as? KtSimpleNameExpression)?.mainReference ?: return
|
||||
val nameExpression = reference.expression.takeIf { it !is KtLabelReferenceExpression } ?: return
|
||||
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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 com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.idea.completion.handlers.createKeywordConstructLookupElement
|
||||
import org.jetbrains.kotlin.lexer.KtKeywordToken
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.psiUtil.prevLeaf
|
||||
|
||||
class CompletionKeywordHandlers(handlers: List<CompletionKeywordHandler>) {
|
||||
constructor(vararg handlers: CompletionKeywordHandler): this(handlers.toList())
|
||||
|
||||
private val handlerByKeyword = handlers.associateBy { it.keyword.value }
|
||||
|
||||
fun getHandlerForKeyword(keyword: String): CompletionKeywordHandler? =
|
||||
handlerByKeyword[keyword]
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
object DefaultCompletionKeywordHandlers {
|
||||
private val BREAK_HANDLER = CompletionKeywordHandler(KtTokens.BREAK_KEYWORD) { _, expression, _, _ ->
|
||||
if (expression != null) {
|
||||
breakOrContinueExpressionItems(expression, KtTokens.BREAK_KEYWORD.value)
|
||||
} else emptyList()
|
||||
}
|
||||
|
||||
private val CONTINUE_HANDLER = CompletionKeywordHandler(KtTokens.CONTINUE_KEYWORD) { _, expression, _, _ ->
|
||||
if (expression != null) {
|
||||
breakOrContinueExpressionItems(expression, KtTokens.CONTINUE_KEYWORD.value)
|
||||
} else emptyList()
|
||||
}
|
||||
|
||||
private val CONTRACT_HANDLER = CompletionKeywordHandler(KtTokens.CONTRACT_KEYWORD) { _, _, _, _ ->
|
||||
emptyList()
|
||||
}
|
||||
|
||||
private val GETTER_HANDLER = CompletionKeywordHandler(KtTokens.GET_KEYWORD) { parameters, _, lookupElement, project ->
|
||||
buildList {
|
||||
add(lookupElement)
|
||||
if (!parameters.isUseSiteAnnotationTarget) {
|
||||
add(createKeywordConstructLookupElement(project, KtTokens.GET_KEYWORD.value, "val v:Int get()=caret"))
|
||||
add(
|
||||
createKeywordConstructLookupElement(
|
||||
project,
|
||||
KtTokens.GET_KEYWORD.value,
|
||||
"val v:Int get(){caret}",
|
||||
trimSpacesAroundCaret = true
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val SETTER_HANDLER = CompletionKeywordHandler(KtTokens.SET_KEYWORD) { parameters, _, lookupElement, project ->
|
||||
buildList {
|
||||
add(lookupElement)
|
||||
if (!parameters.isUseSiteAnnotationTarget) {
|
||||
add(createKeywordConstructLookupElement(project, KtTokens.SET_KEYWORD.value, "var v:Int set(value)=caret"))
|
||||
add(
|
||||
createKeywordConstructLookupElement(
|
||||
project,
|
||||
KtTokens.SET_KEYWORD.value,
|
||||
"var v:Int set(value){caret}",
|
||||
trimSpacesAroundCaret = true
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val defaultHandlers = CompletionKeywordHandlers(
|
||||
BREAK_HANDLER, CONTINUE_HANDLER,
|
||||
GETTER_HANDLER, SETTER_HANDLER,
|
||||
CONTRACT_HANDLER,
|
||||
)
|
||||
}
|
||||
|
||||
private val CompletionParameters.isUseSiteAnnotationTarget
|
||||
get() = position.prevLeaf()?.node?.elementType == KtTokens.AT
|
||||
|
||||
abstract class CompletionKeywordHandler(
|
||||
val keyword: KtKeywordToken,
|
||||
) {
|
||||
abstract fun createLookups(
|
||||
parameters: CompletionParameters,
|
||||
expression: KtExpression?,
|
||||
lookup: LookupElement,
|
||||
project: Project
|
||||
): Collection<LookupElement>
|
||||
|
||||
companion object {
|
||||
inline operator fun invoke(
|
||||
keyword: KtKeywordToken,
|
||||
crossinline create: (
|
||||
parameters: CompletionParameters,
|
||||
expression: KtExpression?,
|
||||
lookup: LookupElement,
|
||||
project: Project
|
||||
) -> Collection<LookupElement>
|
||||
) = object : CompletionKeywordHandler(keyword) {
|
||||
override fun createLookups(
|
||||
parameters: CompletionParameters,
|
||||
expression: KtExpression?,
|
||||
lookup: LookupElement,
|
||||
project: Project
|
||||
): Collection<LookupElement> = create(parameters, expression, lookup, project)
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -48,6 +48,7 @@ open class KeywordLookupObject {
|
||||
override fun hashCode(): Int = javaClass.hashCode()
|
||||
}
|
||||
|
||||
|
||||
class KeywordCompletion(private val languageVersionSettingProvider: LanguageVersionSettingProvider) {
|
||||
interface LanguageVersionSettingProvider {
|
||||
fun getLanguageVersionSetting(element: PsiElement): LanguageVersionSettings
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtDeclarationWithBody
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtLabeledExpression
|
||||
import org.jetbrains.kotlin.psi.KtLoopExpression
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||
import org.jetbrains.kotlin.renderer.render
|
||||
import java.util.ArrayList
|
||||
|
||||
fun createKeywordElement(
|
||||
keyword: String,
|
||||
tail: String = "",
|
||||
lookupObject: KeywordLookupObject = KeywordLookupObject()
|
||||
): LookupElementBuilder {
|
||||
var element = LookupElementBuilder.create(lookupObject, keyword + tail)
|
||||
element = element.withPresentableText(keyword)
|
||||
element = element.withBoldness(true)
|
||||
if (tail.isNotEmpty()) {
|
||||
element = element.withTailText(tail, false)
|
||||
}
|
||||
return element
|
||||
}
|
||||
|
||||
fun breakOrContinueExpressionItems(position: KtElement, breakOrContinue: String): Collection<LookupElement> {
|
||||
val result = ArrayList<LookupElement>()
|
||||
|
||||
parentsLoop@
|
||||
for (parent in position.parentsWithSelf) {
|
||||
when (parent) {
|
||||
is KtLoopExpression -> {
|
||||
if (result.isEmpty()) {
|
||||
result.add(createKeywordElement(breakOrContinue))
|
||||
}
|
||||
|
||||
val label = (parent.parent as? KtLabeledExpression)?.getLabelNameAsName()
|
||||
if (label != null) {
|
||||
result.add(createKeywordElement(breakOrContinue, tail = label.labelNameToTail()))
|
||||
}
|
||||
}
|
||||
|
||||
is KtDeclarationWithBody -> break@parentsLoop //TODO: support non-local break's&continue's when they are supported by compiler
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun Name?.labelNameToTail(): String = if (this != null) "@" + render() else ""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user