diff --git a/idea/src/org/jetbrains/jet/plugin/completion/KeywordCompletion.kt b/idea/src/org/jetbrains/jet/plugin/completion/KeywordCompletion.kt index 9a40a9609a9..04fb7390997 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/KeywordCompletion.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/KeywordCompletion.kt @@ -31,6 +31,7 @@ import com.intellij.openapi.project.Project import org.jetbrains.jet.lang.psi.psiUtil.getParentByType import org.jetbrains.jet.lang.psi.psiUtil.prevLeaf import org.jetbrains.jet.plugin.completion.handlers.KotlinKeywordInsertHandler +import org.jetbrains.jet.lang.psi.psiUtil.siblings class KeywordLookupObject(val keyword: String) @@ -47,7 +48,7 @@ object KeywordCompletion { if (!GENERAL_FILTER.isAcceptable(position, position)) return - val parserFilter = buildParserFilter(position) + val parserFilter = buildFilter(position) for (keyword in ALL_KEYWORDS) { if (prefixMatcher.prefixMatches(keyword) && parserFilter(keyword)) { val element = LookupElementBuilder.create(KeywordLookupObject(keyword), keyword) @@ -90,27 +91,27 @@ object KeywordCompletion { } } - private fun buildParserFilter(position: PsiElement): (String) -> Boolean { + private fun buildFilter(position: PsiElement): (String) -> Boolean { var parent = position.getParent() var prevParent = position while (parent != null) { val _parent = parent when (_parent) { is JetBlockExpression -> { - return buildParserFilterWithContextElement("fun foo() { ", prevParent, position) + return buildFilterWithContext("fun foo() { ", prevParent, position) } is JetWithExpressionInitializer -> { val initializer = _parent.getInitializer() if (prevParent == initializer) { - return buildParserFilterWithContextElement("val v = ", initializer, position) + return buildFilterWithContext("val v = ", initializer, position) } } is JetParameter -> { val default = _parent.getDefaultValue() if (prevParent == default) { - return buildParserFilterWithContextElement("val v = ", default, position) + return buildFilterWithContext("val v = ", default, position) } } } @@ -118,29 +119,36 @@ object KeywordCompletion { if (_parent is JetDeclaration) { val scope = _parent.getParent() when (scope) { - is JetClassOrObject -> return buildParserFilterWithContextElement("class X { ", _parent, position) - is JetFile -> return buildParserFilterWithContextElement("", _parent, position) + is JetClassOrObject -> return buildFilterWithReducedContext("class X { ", _parent, position) + is JetFile -> return buildFilterWithReducedContext("", _parent, position) } } - prevParent = parent!! - parent = parent!!.getParent() + prevParent = _parent + parent = _parent.getParent() } - val builder = StringBuilder() - buildReducedContextBefore(builder, position) - return buildParserFilterByText(builder.toString(), position.getProject()) + return buildFilterWithReducedContext("", null, position) } - private fun buildParserFilterWithContextElement(prefixText: String, - contextElement: PsiElement, - position: PsiElement): (String) -> Boolean { + private fun buildFilterWithContext(prefixText: String, + contextElement: PsiElement, + position: PsiElement): (String) -> Boolean { val offset = position.getStartOffsetInAncestor(contextElement) val truncatedContext = contextElement.getText()!!.substring(0, offset) - return buildParserFilterByText(prefixText + truncatedContext, contextElement.getProject()) + return buildFilterByText(prefixText + truncatedContext, contextElement.getProject()) } - private fun buildParserFilterByText(prefixText: String, project: Project): (String) -> Boolean { + private fun buildFilterWithReducedContext(prefixText: String, + contextElement: PsiElement?, + position: PsiElement): (String) -> Boolean { + val builder = StringBuilder() + buildReducedContextBefore(builder, position, contextElement) + return buildFilterByText(prefixText + builder.toString(), position.getProject()) + } + + + private fun buildFilterByText(prefixText: String, project: Project): (String) -> Boolean { val psiFactory = JetPsiFactory(project) return { keyword -> val file = psiFactory.createFile(prefixText + keyword) @@ -158,17 +166,26 @@ object KeywordCompletion { } } - // builds text before position element excluding declarations - private fun buildReducedContextBefore(builder: StringBuilder, position: PsiElement) { + // 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 val parent = position.getParent() ?: return - buildReducedContextBefore(builder, parent) + buildReducedContextBefore(builder, parent, scope) + + val prevDeclaration = position.siblings(forward = false, withItself = false).firstOrNull { it is JetDeclaration } var child = parent.getFirstChild() while (child != position) { - if (child !is JetDeclaration) { + if (child is JetDeclaration) { + if (child == prevDeclaration) { + builder.append(child!!.getText()) //TODO: skip code blocks, class body inside, + } + } + else { builder.append(child!!.getText()) } + child = child!!.getNextSibling() } } diff --git a/idea/testData/completion/keywords/GlobalPropertyAccessors.kt b/idea/testData/completion/keywords/GlobalPropertyAccessors.kt new file mode 100644 index 00000000000..7263811c86f --- /dev/null +++ b/idea/testData/completion/keywords/GlobalPropertyAccessors.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2010-2014 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. + */ + +fun foo() { + bar() +} + +fun bar() { + foo() +} + +var a : Int + + +// EXIST: abstract +// EXIST: annotation +// EXIST: by +// EXIST: class +// EXIST: enum +// EXIST: final +// EXIST: fun +// EXIST: get +// EXIST: in +/*why?*/ +// EXIST: inner +// EXIST: internal +// EXIST: object +// EXIST: open +// EXIST: out +/*why?*/ +// EXIST: override +// EXIST: private +// EXIST: protected +// EXIST: public +// EXIST: set +// EXIST: trait +// EXIST: val +// EXIST: var +// EXIST: vararg +/*why?*/ +// NUMBER: 23 diff --git a/idea/testData/completion/keywords/InConstructorParameterList.kt.todo b/idea/testData/completion/keywords/InConstructorParameterList.kt.todo new file mode 100644 index 00000000000..7dbe3e6bddf --- /dev/null +++ b/idea/testData/completion/keywords/InConstructorParameterList.kt.todo @@ -0,0 +1,12 @@ +class C() + +// EXIST: in +/*TODO*/ +// EXIST: val +// EXIST: var +// EXIST: public +// EXIST: private +// EXIST: protected +// EXIST: internal +// EXIST: override +// NUMBER: 8 diff --git a/idea/tests/org/jetbrains/jet/completion/KeywordCompletionTestGenerated.java b/idea/tests/org/jetbrains/jet/completion/KeywordCompletionTestGenerated.java index 7465bb7f412..5d692ee5dcf 100644 --- a/idea/tests/org/jetbrains/jet/completion/KeywordCompletionTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/completion/KeywordCompletionTestGenerated.java @@ -17,13 +17,9 @@ package org.jetbrains.jet.completion; import com.intellij.testFramework.TestDataPath; -import junit.framework.Test; -import junit.framework.TestSuite; -import org.junit.runner.RunWith; import org.jetbrains.jet.JetTestUtils; -import org.jetbrains.jet.test.InnerTestClasses; import org.jetbrains.jet.test.TestMetadata; -import org.jetbrains.jet.JUnit3RunnerWithInners; +import org.junit.runner.RunWith; import java.io.File; import java.util.regex.Pattern; @@ -74,6 +70,12 @@ public class KeywordCompletionTestGenerated extends AbstractKeywordCompletionTes doTest(fileName); } + @TestMetadata("GlobalPropertyAccessors.kt") + public void testGlobalPropertyAccessors() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/keywords/GlobalPropertyAccessors.kt"); + doTest(fileName); + } + @TestMetadata("InArgumentList.kt") public void testInArgumentList() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/keywords/InArgumentList.kt");