KTIJ-650 [Code completion]: no "sealed" for classes with modifiers
annotation, data, enum, inner, open - classes supplied with these modifiers cannot be sealed. Commit fixes code completion - "sealed" is no longer suggested in the mentioned case.
This commit is contained in:
@@ -66,17 +66,32 @@ object KeywordCompletion {
|
|||||||
private val KEYWORDS_TO_IGNORE_PREFIX =
|
private val KEYWORDS_TO_IGNORE_PREFIX =
|
||||||
TokenSet.create(OVERRIDE_KEYWORD /* it's needed to complete overrides that should be work by member name too */)
|
TokenSet.create(OVERRIDE_KEYWORD /* it's needed to complete overrides that should be work by member name too */)
|
||||||
|
|
||||||
|
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 = mapOf<KtKeywordToken, Set<KtKeywordToken>>(
|
private val COMPOUND_KEYWORDS = mapOf<KtKeywordToken, Set<KtKeywordToken>>(
|
||||||
COMPANION_KEYWORD to setOf(OBJECT_KEYWORD),
|
COMPANION_KEYWORD to setOf(OBJECT_KEYWORD),
|
||||||
DATA_KEYWORD to setOf(CLASS_KEYWORD),
|
DATA_KEYWORD to setOf(CLASS_KEYWORD),
|
||||||
ENUM_KEYWORD to setOf(CLASS_KEYWORD),
|
ENUM_KEYWORD to setOf(CLASS_KEYWORD),
|
||||||
ANNOTATION_KEYWORD to setOf(CLASS_KEYWORD),
|
ANNOTATION_KEYWORD to setOf(CLASS_KEYWORD),
|
||||||
SEALED_KEYWORD to setOf(CLASS_KEYWORD, INTERFACE_KEYWORD),
|
SEALED_KEYWORD to setOf(CLASS_KEYWORD, INTERFACE_KEYWORD, FUN_KEYWORD),
|
||||||
LATEINIT_KEYWORD to setOf(VAR_KEYWORD),
|
LATEINIT_KEYWORD to setOf(VAR_KEYWORD),
|
||||||
CONST_KEYWORD to setOf(VAL_KEYWORD),
|
CONST_KEYWORD to setOf(VAL_KEYWORD),
|
||||||
SUSPEND_KEYWORD to setOf(FUN_KEYWORD)
|
SUSPEND_KEYWORD to setOf(FUN_KEYWORD)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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 KEYWORD_CONSTRUCTS = mapOf<KtKeywordToken, String>(
|
private val KEYWORD_CONSTRUCTS = mapOf<KtKeywordToken, String>(
|
||||||
IF_KEYWORD to "fun foo() { if (caret)",
|
IF_KEYWORD to "fun foo() { if (caret)",
|
||||||
WHILE_KEYWORD to "fun foo() { while(caret)",
|
WHILE_KEYWORD to "fun foo() { while(caret)",
|
||||||
@@ -105,7 +120,6 @@ object KeywordCompletion {
|
|||||||
SET_KEYWORD
|
SET_KEYWORD
|
||||||
).map { it.value } + "companion object"
|
).map { it.value } + "companion object"
|
||||||
|
|
||||||
|
|
||||||
fun complete(position: PsiElement, prefixMatcher: PrefixMatcher, isJvmModule: Boolean, consumer: (LookupElement) -> Unit) {
|
fun complete(position: PsiElement, prefixMatcher: PrefixMatcher, isJvmModule: Boolean, consumer: (LookupElement) -> Unit) {
|
||||||
if (!GENERAL_FILTER.isAcceptable(position, position)) return
|
if (!GENERAL_FILTER.isAcceptable(position, position)) return
|
||||||
|
|
||||||
@@ -125,6 +139,11 @@ object KeywordCompletion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun KtKeywordToken.avoidSuggestingWith(keywordToken: KtKeywordToken): Boolean {
|
||||||
|
val nextKeywords = COMPOUND_KEYWORDS_NOT_SUGGEST_TOGETHER[this] ?: return false
|
||||||
|
return keywordToken in nextKeywords
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleCompoundKeyword(
|
private fun handleCompoundKeyword(
|
||||||
position: PsiElement,
|
position: PsiElement,
|
||||||
keywordToken: KtKeywordToken,
|
keywordToken: KtKeywordToken,
|
||||||
@@ -143,7 +162,15 @@ object KeywordCompletion {
|
|||||||
var next = position.nextLeaf { !(it.isSpace() || it.text == "$") }?.text
|
var next = position.nextLeaf { !(it.isSpace() || it.text == "$") }?.text
|
||||||
next = next?.removePrefix("$")
|
next = next?.removePrefix("$")
|
||||||
|
|
||||||
|
if (keywordToken == SEALED_KEYWORD) {
|
||||||
|
if (next in INCOMPATIBLE_KEYWORDS_AROUND_SEALED) return
|
||||||
|
val prev = position.prevLeaf { !(it.isSpace() || it is PsiErrorElement) }?.text
|
||||||
|
if (prev in INCOMPATIBLE_KEYWORDS_AROUND_SEALED) return
|
||||||
|
}
|
||||||
|
|
||||||
val nextIsNotYetPresent = keywordToken.getNextPossibleKeywords(position)?.none { it.value == next } == true
|
val nextIsNotYetPresent = keywordToken.getNextPossibleKeywords(position)?.none { it.value == next } == true
|
||||||
|
if (nextIsNotYetPresent && keywordToken.avoidSuggestingWith(nextKeyword)) return
|
||||||
|
|
||||||
if (nextIsNotYetPresent)
|
if (nextIsNotYetPresent)
|
||||||
keyword += " " + nextKeyword.value
|
keyword += " " + nextKeyword.value
|
||||||
else
|
else
|
||||||
@@ -609,4 +636,4 @@ object KeywordCompletion {
|
|||||||
else -> it.parent
|
else -> it.parent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
seal<caret> sealed class A
|
||||||
|
// ABSENT: "sealed"
|
||||||
|
// ABSENT: "sealed class"
|
||||||
|
// ABSENT: "sealed interface"
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
seal<caret> annotation class A
|
||||||
|
// ABSENT: "sealed"
|
||||||
|
// ABSENT: "sealed class"
|
||||||
|
// ABSENT: "sealed interface"
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
seal<caret> data class A(val f: Int)
|
||||||
|
// ABSENT: "sealed"
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
seal<caret> enum class A
|
||||||
|
// ABSENT: "sealed"
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
seal<caret> fun interface A {
|
||||||
|
fun aFunction()
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXIST: "sealed"
|
||||||
|
// NOTHING_ELSE
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
class A {
|
||||||
|
seal<caret>inner class B
|
||||||
|
}
|
||||||
|
// ABSENT: "sealed"
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
seal<caret> open class A
|
||||||
|
// ABSENT: "sealed"
|
||||||
+35
@@ -503,6 +503,21 @@ public class KeywordCompletionTestGenerated extends AbstractKeywordCompletionTes
|
|||||||
runTest("idea/idea-completion/testData/keywords/ReturnSet.kt");
|
runTest("idea/idea-completion/testData/keywords/ReturnSet.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("SealedForAlreadySealed.kt")
|
||||||
|
public void testSealedForAlreadySealed() throws Exception {
|
||||||
|
runTest("idea/idea-completion/testData/keywords/SealedForAlreadySealed.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("SealedForAnnotationClass.kt")
|
||||||
|
public void testSealedForAnnotationClass() throws Exception {
|
||||||
|
runTest("idea/idea-completion/testData/keywords/SealedForAnnotationClass.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("SealedForDataClass.kt")
|
||||||
|
public void testSealedForDataClass() throws Exception {
|
||||||
|
runTest("idea/idea-completion/testData/keywords/SealedForDataClass.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("SealedForDeclaredClass.kt")
|
@TestMetadata("SealedForDeclaredClass.kt")
|
||||||
public void testSealedForDeclaredClass() throws Exception {
|
public void testSealedForDeclaredClass() throws Exception {
|
||||||
runTest("idea/idea-completion/testData/keywords/SealedForDeclaredClass.kt");
|
runTest("idea/idea-completion/testData/keywords/SealedForDeclaredClass.kt");
|
||||||
@@ -513,6 +528,26 @@ public class KeywordCompletionTestGenerated extends AbstractKeywordCompletionTes
|
|||||||
runTest("idea/idea-completion/testData/keywords/SealedForDeclaredInterface.kt");
|
runTest("idea/idea-completion/testData/keywords/SealedForDeclaredInterface.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("SealedForEnumClass.kt")
|
||||||
|
public void testSealedForEnumClass() throws Exception {
|
||||||
|
runTest("idea/idea-completion/testData/keywords/SealedForEnumClass.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("SealedForFunInterface.kt")
|
||||||
|
public void testSealedForFunInterface() throws Exception {
|
||||||
|
runTest("idea/idea-completion/testData/keywords/SealedForFunInterface.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("SealedForInnerClass.kt")
|
||||||
|
public void testSealedForInnerClass() throws Exception {
|
||||||
|
runTest("idea/idea-completion/testData/keywords/SealedForInnerClass.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("SealedForOpenClass.kt")
|
||||||
|
public void testSealedForOpenClass() throws Exception {
|
||||||
|
runTest("idea/idea-completion/testData/keywords/SealedForOpenClass.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("SealedWithName.kt")
|
@TestMetadata("SealedWithName.kt")
|
||||||
public void testSealedWithName() throws Exception {
|
public void testSealedWithName() throws Exception {
|
||||||
runTest("idea/idea-completion/testData/keywords/SealedWithName.kt");
|
runTest("idea/idea-completion/testData/keywords/SealedWithName.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user