diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KeywordCompletion.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KeywordCompletion.kt index b2e82e349da..365aef67f5f 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KeywordCompletion.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KeywordCompletion.kt @@ -20,7 +20,7 @@ import com.intellij.codeInsight.completion.InsertHandler import com.intellij.codeInsight.completion.InsertionContext import com.intellij.codeInsight.lookup.LookupElement import com.intellij.codeInsight.lookup.LookupElementBuilder -import com.intellij.openapi.project.Project +import com.intellij.openapi.module.ModuleUtilCore import com.intellij.psi.PsiComment import com.intellij.psi.PsiElement import com.intellij.psi.PsiErrorElement @@ -30,10 +30,13 @@ import com.intellij.psi.filters.position.LeftNeighbour import com.intellij.psi.filters.position.PositionElementFilter import com.intellij.psi.tree.IElementType import com.intellij.psi.tree.TokenSet +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl 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 @@ -255,7 +258,7 @@ object KeywordCompletion { position: PsiElement): (KtKeywordToken) -> Boolean { val offset = position.getStartOffsetInAncestor(contextElement) val truncatedContext = contextElement.text!!.substring(0, offset) - return buildFilterByText(prefixText + truncatedContext, contextElement.project) + return buildFilterByText(prefixText + truncatedContext, position) } private fun buildFilterWithReducedContext(prefixText: String, @@ -263,12 +266,12 @@ object KeywordCompletion { position: PsiElement): (KtKeywordToken) -> Boolean { val builder = StringBuilder() buildReducedContextBefore(builder, position, contextElement) - return buildFilterByText(prefixText + builder.toString(), position.project) + return buildFilterByText(prefixText + builder.toString(), position) } - private fun buildFilterByText(prefixText: String, project: Project): (KtKeywordToken) -> Boolean { - val psiFactory = KtPsiFactory(project) + private fun buildFilterByText(prefixText: String, position: PsiElement): (KtKeywordToken) -> Boolean { + val psiFactory = KtPsiFactory(position.project) return fun (keywordTokenType): Boolean { val postfix = if (prefixText.endsWith("@")) ":X Y.Z" else " X" val file = psiFactory.createFile(prefixText + keywordTokenType.value + postfix) @@ -281,6 +284,8 @@ object KeywordCompletion { isErrorElementBefore(elementAt) -> return false + !isSupportedAtLanguageLevel(keywordTokenType, position) -> return false + keywordTokenType !is KtModifierKeywordToken -> return true else -> { @@ -356,6 +361,17 @@ object KeywordCompletion { } } + private fun isSupportedAtLanguageLevel(keyword: KtKeywordToken, position: PsiElement): Boolean { + val languageVersionSettings = ModuleUtilCore.findModuleForPsiElement(position)?.languageVersionSettings ?: LanguageVersionSettingsImpl.DEFAULT + val feature = when (keyword) { + KtTokens.TYPE_ALIAS_KEYWORD -> LanguageFeature.TypeAliases + KtTokens.HEADER_KEYWORD, KtTokens.IMPL_KEYWORD -> LanguageFeature.MultiPlatformProjects + KtTokens.SUSPEND_KEYWORD -> LanguageFeature.Coroutines + else -> return true + } + return languageVersionSettings.supportsFeature(feature) + } + // builds text within scope (or from the start of the file) before position element excluding almost all declarations private fun buildReducedContextBefore(builder: StringBuilder, position: PsiElement, scope: PsiElement?) { if (position == scope) return diff --git a/idea/idea-completion/testData/keywords/AfterClasses_LangLevel10.kt b/idea/idea-completion/testData/keywords/AfterClasses_LangLevel10.kt new file mode 100644 index 00000000000..6b906e958da --- /dev/null +++ b/idea/idea-completion/testData/keywords/AfterClasses_LangLevel10.kt @@ -0,0 +1,37 @@ +class A { + fun foo() { + bar() + } +} + +class B { + fun bar() { + foo() + } +} + + + +// EXIST: abstract +// EXIST: class +// EXIST: enum class +// EXIST: final +// EXIST: fun +// EXIST: internal +// EXIST: object +// EXIST: open +// EXIST: private +// EXIST: public +// EXIST: interface +// EXIST: val +// EXIST: var +// EXIST: operator +// EXIST: infix +// EXIST: sealed +// EXIST: data +// EXIST: inline +// EXIST: tailrec +// EXIST: external +// EXIST: annotation class +// EXIST: const +// NOTHING_ELSE diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/AbstractKeywordCompletionTest.kt b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/AbstractKeywordCompletionTest.kt index 8d6e8a452a0..0a34506add7 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/AbstractKeywordCompletionTest.kt +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/AbstractKeywordCompletionTest.kt @@ -20,6 +20,7 @@ import com.intellij.codeInsight.completion.CompletionType import com.intellij.codeInsight.lookup.LookupElement import org.jetbrains.kotlin.idea.completion.KeywordLookupObject import org.jetbrains.kotlin.idea.test.KotlinLightProjectDescriptor +import org.jetbrains.kotlin.idea.test.KotlinProjectDescriptorWithFacet import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform abstract class AbstractKeywordCompletionTest : KotlinFixtureCompletionBaseTestCase() { @@ -32,7 +33,12 @@ abstract class AbstractKeywordCompletionTest : KotlinFixtureCompletionBaseTestCa return items.filter { it.`object` is KeywordLookupObject }.toTypedArray() } - override fun getProjectDescriptor() = KotlinLightProjectDescriptor.INSTANCE + override fun getProjectDescriptor(): KotlinLightProjectDescriptor { + if ("LangLevel10" in fileName()) { + return KotlinProjectDescriptorWithFacet.KOTLIN_10 + } + return KotlinLightProjectDescriptor.INSTANCE + } override fun defaultInvocationCount() = 1 } \ No newline at end of file diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KeywordCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KeywordCompletionTestGenerated.java index ffd09ed8603..991194e6128 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KeywordCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KeywordCompletionTestGenerated.java @@ -44,6 +44,12 @@ public class KeywordCompletionTestGenerated extends AbstractKeywordCompletionTes doTest(fileName); } + @TestMetadata("AfterClasses_LangLevel10.kt") + public void testAfterClasses_LangLevel10() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/keywords/AfterClasses_LangLevel10.kt"); + doTest(fileName); + } + @TestMetadata("AfterDot.kt") public void testAfterDot() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/keywords/AfterDot.kt"); diff --git a/idea/idea-test-framework/idea-test-framework.iml b/idea/idea-test-framework/idea-test-framework.iml index b5c20c17dfc..a6ee8f3f27c 100644 --- a/idea/idea-test-framework/idea-test-framework.iml +++ b/idea/idea-test-framework/idea-test-framework.iml @@ -14,5 +14,6 @@ + \ No newline at end of file diff --git a/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/KotlinProjectDescriptorWithFacet.kt b/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/KotlinProjectDescriptorWithFacet.kt new file mode 100644 index 00000000000..51e3e15ac62 --- /dev/null +++ b/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/KotlinProjectDescriptorWithFacet.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2010-2017 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. + */ + +package org.jetbrains.kotlin.idea.test + +import com.intellij.facet.FacetManager +import com.intellij.openapi.module.Module +import com.intellij.openapi.roots.ContentEntry +import com.intellij.openapi.roots.ModifiableRootModel +import org.jetbrains.kotlin.config.LanguageVersion +import org.jetbrains.kotlin.idea.facet.KotlinFacetConfiguration +import org.jetbrains.kotlin.idea.facet.KotlinFacetType +import org.jetbrains.kotlin.test.testFramework.runInEdtAndWait +import org.jetbrains.kotlin.test.testFramework.runWriteAction + +class KotlinProjectDescriptorWithFacet(val languageVersion: LanguageVersion) : KotlinLightProjectDescriptor() { + override fun configureModule(module: Module, model: ModifiableRootModel, contentEntry: ContentEntry) { + val facetManager = FacetManager.getInstance(module) + val facetModel = facetManager.createModifiableModel() + val configuration = KotlinFacetConfiguration() + configuration.settings.useProjectSettings = false + configuration.settings.versionInfo.languageLevel = languageVersion + val facet = facetManager.createFacet(KotlinFacetType.INSTANCE, "Kotlin", configuration, null) + facetModel.addFacet(facet) + runInEdtAndWait { + runWriteAction { facetModel.commit() } + } + } + + companion object { + val KOTLIN_10 = KotlinProjectDescriptorWithFacet(LanguageVersion.KOTLIN_1_0) + } +}