Move keyword completion stuff to frontend-independent module
This commit is contained in:
committed by
TeamCityServer
parent
d7a91cb05e
commit
62fe3930ff
+9
-1
@@ -13,12 +13,14 @@ import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||
import com.intellij.codeInsight.lookup.LookupElementDecorator
|
||||
import com.intellij.codeInsight.lookup.LookupElementPresentation
|
||||
import com.intellij.codeInsight.template.TemplateManager
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.patterns.PatternCondition
|
||||
import com.intellij.patterns.StandardPatterns
|
||||
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
|
||||
@@ -33,6 +35,7 @@ import org.jetbrains.kotlin.idea.core.NotPropertiesService
|
||||
import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject
|
||||
import org.jetbrains.kotlin.idea.imports.importableFqName
|
||||
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
|
||||
import org.jetbrains.kotlin.idea.project.languageVersionSettings
|
||||
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference.ShorteningMode.FORCED_SHORTENING
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil
|
||||
@@ -539,6 +542,11 @@ class BasicCompletionSession(
|
||||
override val descriptorKindFilter: DescriptorKindFilter?
|
||||
get() = null
|
||||
|
||||
private val keywordCompletion = KeywordCompletion(object : KeywordCompletion.LanguageVersionSettingProvider {
|
||||
override fun getLanguageVersionSetting(element: PsiElement) = element.languageVersionSettings
|
||||
override fun getLanguageVersionSetting(module: Module) = module.languageVersionSettings
|
||||
})
|
||||
|
||||
override fun doComplete() {
|
||||
val keywordsToSkip = HashSet<String>()
|
||||
|
||||
@@ -574,7 +582,7 @@ 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 ->
|
||||
keywordCompletion.complete(expression ?: parameters.position, resultSet.prefixMatcher, isJvmModule) { lookupElement ->
|
||||
val keyword = lookupElement.lookupString
|
||||
if (keyword in keywordsToSkip) return@complete
|
||||
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.handlers
|
||||
|
||||
import com.intellij.codeInsight.completion.InsertionContext
|
||||
import com.intellij.codeInsight.lookup.Lookup
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import org.jetbrains.kotlin.idea.completion.KEEP_OLD_ARGUMENT_LIST_ON_TAB_KEY
|
||||
import org.jetbrains.kotlin.idea.completion.smart.SmartCompletion
|
||||
import org.jetbrains.kotlin.idea.completion.tryGetOffset
|
||||
|
||||
class SmartCompletionTailOffsetProviderFE10Impl: SmartCompletionTailOffsetProvider() {
|
||||
override fun getTailOffset(context: InsertionContext, item: LookupElement): Int {
|
||||
val completionChar = context.completionChar
|
||||
var tailOffset = context.tailOffset
|
||||
if (completionChar == Lookup.REPLACE_SELECT_CHAR && item.getUserData(KEEP_OLD_ARGUMENT_LIST_ON_TAB_KEY) != null) {
|
||||
context.offsetMap.tryGetOffset(SmartCompletion.OLD_ARGUMENTS_REPLACEMENT_OFFSET)
|
||||
?.let { tailOffset = it }
|
||||
}
|
||||
return tailOffset
|
||||
}
|
||||
}
|
||||
+68
-72
@@ -1,17 +1,6 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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
|
||||
@@ -21,6 +10,7 @@ import com.intellij.codeInsight.completion.InsertionContext
|
||||
import com.intellij.codeInsight.completion.PrefixMatcher
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.ModuleUtilCore
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.psi.*
|
||||
@@ -37,7 +27,6 @@ import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
|
||||
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget.*
|
||||
import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler
|
||||
import org.jetbrains.kotlin.idea.completion.handlers.createKeywordConstructLookupElement
|
||||
import org.jetbrains.kotlin.idea.project.languageVersionSettings
|
||||
import org.jetbrains.kotlin.lexer.KtKeywordToken
|
||||
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
||||
import org.jetbrains.kotlin.lexer.KtTokens.*
|
||||
@@ -59,70 +48,77 @@ open class KeywordLookupObject {
|
||||
override fun hashCode(): Int = javaClass.hashCode()
|
||||
}
|
||||
|
||||
object KeywordCompletion {
|
||||
private val ALL_KEYWORDS = (KEYWORDS.types + SOFT_KEYWORDS.types)
|
||||
.map { it as KtKeywordToken }
|
||||
class KeywordCompletion(private val languageVersionSettingProvider: LanguageVersionSettingProvider) {
|
||||
interface LanguageVersionSettingProvider {
|
||||
fun getLanguageVersionSetting(element: PsiElement): LanguageVersionSettings
|
||||
fun getLanguageVersionSetting(module: Module): LanguageVersionSettings
|
||||
}
|
||||
|
||||
private val KEYWORDS_TO_IGNORE_PREFIX =
|
||||
TokenSet.create(OVERRIDE_KEYWORD /* it's needed to complete overrides that should be work by member name too */)
|
||||
companion object {
|
||||
private val ALL_KEYWORDS = (KEYWORDS.types + SOFT_KEYWORDS.types)
|
||||
.map { it as KtKeywordToken }
|
||||
|
||||
private val INCOMPATIBLE_KEYWORDS_AROUND_SEALED = setOf(
|
||||
SEALED_KEYWORD,
|
||||
ANNOTATION_KEYWORD,
|
||||
DATA_KEYWORD,
|
||||
ENUM_KEYWORD,
|
||||
OPEN_KEYWORD,
|
||||
INNER_KEYWORD,
|
||||
ABSTRACT_KEYWORD
|
||||
).mapTo(HashSet()) { it.value }
|
||||
private val KEYWORDS_TO_IGNORE_PREFIX =
|
||||
TokenSet.create(OVERRIDE_KEYWORD /* it's needed to complete overrides that should be work by member name too */)
|
||||
|
||||
private val COMPOUND_KEYWORDS = mapOf<KtKeywordToken, Set<KtKeywordToken>>(
|
||||
COMPANION_KEYWORD to setOf(OBJECT_KEYWORD),
|
||||
DATA_KEYWORD to setOf(CLASS_KEYWORD),
|
||||
ENUM_KEYWORD to setOf(CLASS_KEYWORD),
|
||||
ANNOTATION_KEYWORD to setOf(CLASS_KEYWORD),
|
||||
SEALED_KEYWORD to setOf(CLASS_KEYWORD, INTERFACE_KEYWORD, FUN_KEYWORD),
|
||||
LATEINIT_KEYWORD to setOf(VAR_KEYWORD),
|
||||
CONST_KEYWORD to setOf(VAL_KEYWORD),
|
||||
SUSPEND_KEYWORD to setOf(FUN_KEYWORD)
|
||||
)
|
||||
private val INCOMPATIBLE_KEYWORDS_AROUND_SEALED = setOf(
|
||||
SEALED_KEYWORD,
|
||||
ANNOTATION_KEYWORD,
|
||||
DATA_KEYWORD,
|
||||
ENUM_KEYWORD,
|
||||
OPEN_KEYWORD,
|
||||
INNER_KEYWORD,
|
||||
ABSTRACT_KEYWORD
|
||||
).mapTo(HashSet()) { it.value }
|
||||
|
||||
private val COMPOUND_KEYWORDS_NOT_SUGGEST_TOGETHER = mapOf<KtKeywordToken, Set<KtKeywordToken>>(
|
||||
// 'fun' can follow 'sealed', e.g. "sealed fun interface". But "sealed fun" looks irrelevant differ to "sealed interface/class".
|
||||
SEALED_KEYWORD to setOf(FUN_KEYWORD),
|
||||
)
|
||||
private val COMPOUND_KEYWORDS = mapOf<KtKeywordToken, Set<KtKeywordToken>>(
|
||||
COMPANION_KEYWORD to setOf(OBJECT_KEYWORD),
|
||||
DATA_KEYWORD to setOf(CLASS_KEYWORD),
|
||||
ENUM_KEYWORD to setOf(CLASS_KEYWORD),
|
||||
ANNOTATION_KEYWORD to setOf(CLASS_KEYWORD),
|
||||
SEALED_KEYWORD to setOf(CLASS_KEYWORD, INTERFACE_KEYWORD, FUN_KEYWORD),
|
||||
LATEINIT_KEYWORD to setOf(VAR_KEYWORD),
|
||||
CONST_KEYWORD to setOf(VAL_KEYWORD),
|
||||
SUSPEND_KEYWORD to setOf(FUN_KEYWORD)
|
||||
)
|
||||
|
||||
private val KEYWORD_CONSTRUCTS = mapOf<KtKeywordToken, String>(
|
||||
IF_KEYWORD to "fun foo() { if (caret)",
|
||||
WHILE_KEYWORD to "fun foo() { while(caret)",
|
||||
FOR_KEYWORD to "fun foo() { for(caret)",
|
||||
TRY_KEYWORD to "fun foo() { try {\ncaret\n}",
|
||||
CATCH_KEYWORD to "fun foo() { try {} catch (caret)",
|
||||
FINALLY_KEYWORD to "fun foo() { try {\n}\nfinally{\ncaret\n}",
|
||||
DO_KEYWORD to "fun foo() { do {\ncaret\n}",
|
||||
INIT_KEYWORD to "class C { init {\ncaret\n}",
|
||||
CONSTRUCTOR_KEYWORD to "class C { constructor(caret)"
|
||||
)
|
||||
private val COMPOUND_KEYWORDS_NOT_SUGGEST_TOGETHER = mapOf<KtKeywordToken, Set<KtKeywordToken>>(
|
||||
// 'fun' can follow 'sealed', e.g. "sealed fun interface". But "sealed fun" looks irrelevant differ to "sealed interface/class".
|
||||
SEALED_KEYWORD to setOf(FUN_KEYWORD),
|
||||
)
|
||||
|
||||
private val NO_SPACE_AFTER = listOf(
|
||||
THIS_KEYWORD,
|
||||
SUPER_KEYWORD,
|
||||
NULL_KEYWORD,
|
||||
TRUE_KEYWORD,
|
||||
FALSE_KEYWORD,
|
||||
BREAK_KEYWORD,
|
||||
CONTINUE_KEYWORD,
|
||||
ELSE_KEYWORD,
|
||||
WHEN_KEYWORD,
|
||||
FILE_KEYWORD,
|
||||
DYNAMIC_KEYWORD,
|
||||
GET_KEYWORD,
|
||||
SET_KEYWORD
|
||||
).map { it.value } + "companion object"
|
||||
private val KEYWORD_CONSTRUCTS = mapOf<KtKeywordToken, String>(
|
||||
IF_KEYWORD to "fun foo() { if (caret)",
|
||||
WHILE_KEYWORD to "fun foo() { while(caret)",
|
||||
FOR_KEYWORD to "fun foo() { for(caret)",
|
||||
TRY_KEYWORD to "fun foo() { try {\ncaret\n}",
|
||||
CATCH_KEYWORD to "fun foo() { try {} catch (caret)",
|
||||
FINALLY_KEYWORD to "fun foo() { try {\n}\nfinally{\ncaret\n}",
|
||||
DO_KEYWORD to "fun foo() { do {\ncaret\n}",
|
||||
INIT_KEYWORD to "class C { init {\ncaret\n}",
|
||||
CONSTRUCTOR_KEYWORD to "class C { constructor(caret)"
|
||||
)
|
||||
|
||||
private val NO_SPACE_AFTER = listOf(
|
||||
THIS_KEYWORD,
|
||||
SUPER_KEYWORD,
|
||||
NULL_KEYWORD,
|
||||
TRUE_KEYWORD,
|
||||
FALSE_KEYWORD,
|
||||
BREAK_KEYWORD,
|
||||
CONTINUE_KEYWORD,
|
||||
ELSE_KEYWORD,
|
||||
WHEN_KEYWORD,
|
||||
FILE_KEYWORD,
|
||||
DYNAMIC_KEYWORD,
|
||||
GET_KEYWORD,
|
||||
SET_KEYWORD
|
||||
).map { it.value } + "companion object"
|
||||
}
|
||||
|
||||
fun complete(position: PsiElement, prefixMatcher: PrefixMatcher, isJvmModule: Boolean, consumer: (LookupElement) -> Unit) {
|
||||
if (!GENERAL_FILTER.isAcceptable(position, position)) return
|
||||
val sealedInterfacesEnabled = position.languageVersionSettings.supportsFeature(LanguageFeature.SealedInterfaces)
|
||||
val sealedInterfacesEnabled = languageVersionSettingProvider.getLanguageVersionSetting(position).supportsFeature(LanguageFeature.SealedInterfaces)
|
||||
|
||||
val parserFilter = buildFilter(position)
|
||||
for (keywordToken in ALL_KEYWORDS) {
|
||||
@@ -422,9 +418,9 @@ object KeywordCompletion {
|
||||
fun isKeywordCorrectlyApplied(keywordTokenType: KtKeywordToken, file: KtFile): Boolean {
|
||||
val elementAt = file.findElementAt(prefixText.length)!!
|
||||
|
||||
val languageVersionSettings = ModuleUtilCore.findModuleForPsiElement(position)?.languageVersionSettings
|
||||
?: LanguageVersionSettingsImpl.DEFAULT
|
||||
|
||||
val languageVersionSettings =
|
||||
ModuleUtilCore.findModuleForPsiElement(position)?.let(languageVersionSettingProvider::getLanguageVersionSetting)
|
||||
?: LanguageVersionSettingsImpl.DEFAULT
|
||||
when {
|
||||
!elementAt.node!!.elementType.matchesKeyword(keywordTokenType) -> return false
|
||||
|
||||
+11
-23
@@ -1,17 +1,6 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.handlers
|
||||
@@ -19,13 +8,15 @@ package org.jetbrains.kotlin.idea.completion.handlers
|
||||
import com.intellij.codeInsight.AutoPopupController
|
||||
import com.intellij.codeInsight.completion.InsertHandler
|
||||
import com.intellij.codeInsight.completion.InsertionContext
|
||||
import com.intellij.codeInsight.lookup.Lookup
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.openapi.components.serviceOrNull
|
||||
import com.intellij.openapi.editor.Document
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import org.jetbrains.kotlin.idea.completion.KEEP_OLD_ARGUMENT_LIST_ON_TAB_KEY
|
||||
import org.jetbrains.kotlin.idea.completion.smart.SmartCompletion
|
||||
import org.jetbrains.kotlin.idea.completion.tryGetOffset
|
||||
|
||||
|
||||
abstract class SmartCompletionTailOffsetProvider {
|
||||
abstract fun getTailOffset(context: InsertionContext, item: LookupElement): Int
|
||||
}
|
||||
|
||||
class WithTailInsertHandler(
|
||||
val tailText: String,
|
||||
@@ -48,11 +39,8 @@ class WithTailInsertHandler(
|
||||
val document = context.document
|
||||
PsiDocumentManager.getInstance(context.project).doPostponedOperationsAndUnblockDocument(document)
|
||||
|
||||
var tailOffset = context.tailOffset
|
||||
if (completionChar == Lookup.REPLACE_SELECT_CHAR && item.getUserData(KEEP_OLD_ARGUMENT_LIST_ON_TAB_KEY) != null) {
|
||||
context.offsetMap.tryGetOffset(SmartCompletion.OLD_ARGUMENTS_REPLACEMENT_OFFSET)
|
||||
?.let { tailOffset = it }
|
||||
}
|
||||
var tailOffset = serviceOrNull<SmartCompletionTailOffsetProvider>()?.getTailOffset(context, item)
|
||||
?:context.tailOffset
|
||||
|
||||
val moveCaret = context.editor.caretModel.offset == tailOffset
|
||||
|
||||
@@ -107,4 +95,4 @@ class WithTailInsertHandler(
|
||||
val EQ = WithTailInsertHandler("=", spaceBefore = true, spaceAfter = true) /*TODO: use code style options*/
|
||||
val SPACE = WithTailInsertHandler(" ", spaceBefore = false, spaceAfter = false, overwriteText = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,11 +150,6 @@ fun Editor.unblockDocument() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Editor.moveCaret(offset: Int, scrollType: ScrollType = ScrollType.RELATIVE) {
|
||||
caretModel.moveToOffset(offset)
|
||||
scrollingModel.scrollToCaret(scrollType)
|
||||
}
|
||||
|
||||
private fun findInsertAfterAnchor(editor: Editor?, body: KtClassBody): PsiElement? {
|
||||
val afterAnchor = body.lBrace ?: return null
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* 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.core
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.ScrollType
|
||||
|
||||
|
||||
fun Editor.moveCaret(offset: Int, scrollType: ScrollType = ScrollType.RELATIVE) {
|
||||
caretModel.moveToOffset(offset)
|
||||
scrollingModel.scrollToCaret(scrollType)
|
||||
}
|
||||
@@ -148,7 +148,8 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<pathMacroContributor implementation="org.jetbrains.kotlin.idea.KotlinPluginMacros"/>
|
||||
<applicationService serviceImplementation="org.jetbrains.kotlin.idea.PluginStartupApplicationService" />
|
||||
|
||||
<applicationService serviceInterface="org.jetbrains.kotlin.idea.completion.handlers.SmartCompletionTailOffsetProvider"
|
||||
serviceImplementation="org.jetbrains.kotlin.idea.completion.handlers.SmartCompletionTailOffsetProviderFE10Impl"/>
|
||||
<postStartupActivity implementation="org.jetbrains.kotlin.idea.PluginStartupActivity"/>
|
||||
<projectService serviceImplementation="org.jetbrains.kotlin.idea.PluginStartupService"/>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user