Keyword correction: more correct behavior + smaller file text

This commit is contained in:
Valentin Kipyatkov
2014-10-14 22:03:04 +04:00
parent 525a1cc3c6
commit f7dc0fca74
4 changed files with 111 additions and 26 deletions
@@ -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()
}
}
@@ -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
<caret>
// 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
@@ -0,0 +1,12 @@
class C(<caret>)
// EXIST: in
/*TODO*/
// EXIST: val
// EXIST: var
// EXIST: public
// EXIST: private
// EXIST: protected
// EXIST: internal
// EXIST: override
// NUMBER: 8
@@ -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");