KT-7349 Replace keyword "companion" in completion with "companion object"

This commit is contained in:
Valentin Kipyatkov
2015-05-14 18:15:52 +03:00
parent db40f1ee5a
commit 8ea0441348
22 changed files with 109 additions and 64 deletions
@@ -16,35 +16,26 @@
package org.jetbrains.kotlin.psi.psiUtil
import org.jetbrains.kotlin.psi.*
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.lexer.JetTokens
import java.util.Collections
import com.intellij.extapi.psi.StubBasedPsiElementBase
import java.util.ArrayList
import kotlin.test.assertTrue
import com.intellij.psi.search.SearchScope
import com.intellij.psi.search.PsiSearchScopeUtil
import com.intellij.psi.PsiParameterList
import com.intellij.psi.PsiParameter
import com.intellij.psi.PsiPackage
import com.intellij.psi.JavaDirectoryService
import com.intellij.psi.PsiDirectory
import org.jetbrains.kotlin.psi.stubs.KotlinClassOrObjectStub
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
import com.intellij.lang.ASTNode
import com.intellij.openapi.util.Condition
import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.PsiComment
import org.jetbrains.kotlin.resolve.calls.CallTransformer.CallForImplicitInvoke
import com.intellij.openapi.util.TextRange
import com.intellij.psi.*
import com.intellij.psi.search.PsiSearchScopeUtil
import com.intellij.psi.search.SearchScope
import com.intellij.psi.stubs.StubElement
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.JetNodeTypes
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.stubs.KotlinClassOrObjectStub
import org.jetbrains.kotlin.resolve.calls.CallTransformer.CallForImplicitInvoke
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.utils.addToStdlib.lastIsInstanceOrNull
import java.util.ArrayList
import java.util.Collections
import kotlin.test.assertTrue
public fun JetCallElement.getCallNameExpression(): JetSimpleNameExpression? {
val calleeExpression = getCalleeExpression() ?: return null
@@ -419,25 +410,17 @@ public fun PsiElement.prevLeaf(skipEmptyElements: Boolean = false): PsiElement?
public fun PsiElement.nextLeaf(skipEmptyElements: Boolean = false): PsiElement?
= PsiTreeUtil.nextLeaf(this, skipEmptyElements)
public fun PsiElement.prevLeafSkipWhitespacesAndComments(): PsiElement? {
public fun PsiElement.prevLeaf(filter: (PsiElement) -> Boolean): PsiElement? {
var leaf = prevLeaf()
while (leaf is PsiWhiteSpace || leaf is PsiComment) {
while (leaf != null && !filter(leaf)) {
leaf = leaf.prevLeaf()
}
return leaf
}
public fun PsiElement.prevLeafSkipWhitespaces(): PsiElement? {
var leaf = prevLeaf()
while (leaf is PsiWhiteSpace) {
leaf = leaf.prevLeaf()
}
return leaf
}
public fun PsiElement.nextLeafSkipWhitespacesAndComments(): PsiElement? {
public fun PsiElement.nextLeaf(filter: (PsiElement) -> Boolean): PsiElement? {
var leaf = nextLeaf()
while (leaf is PsiWhiteSpace || leaf is PsiComment) {
while (leaf != null && !filter(leaf)) {
leaf = leaf.nextLeaf()
}
return leaf
@@ -19,8 +19,10 @@ package org.jetbrains.kotlin.idea.completion
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiErrorElement
import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.filters.*
import com.intellij.psi.filters.position.LeftNeighbour
import com.intellij.psi.filters.position.PositionElementFilter
@@ -31,7 +33,8 @@ import org.jetbrains.kotlin.lexer.JetKeywordToken
import org.jetbrains.kotlin.lexer.JetTokens.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.prevLeafSkipWhitespacesAndComments
import org.jetbrains.kotlin.psi.psiUtil.nextLeaf
import org.jetbrains.kotlin.psi.psiUtil.prevLeaf
import org.jetbrains.kotlin.psi.psiUtil.siblings
object KeywordLookupObject
@@ -54,7 +57,20 @@ object KeywordCompletion {
val parserFilter = buildFilter(position)
for (keywordToken in ALL_KEYWORDS) {
val keyword = keywordToken.getValue()
var keyword = keywordToken.getValue()
if (keyword == COMPANION_KEYWORD.getValue()) { // complete "companion object" instead of simply "companion" unless we are before "object" already
fun PsiElement.isSpace() = this is PsiWhiteSpace && '\n' !in getText()
var next = position.nextLeaf { !(it.isSpace() || it.getText() == "$") }?.getText()
if (next != null && next.startsWith("$")) {
next = next.substring(1)
}
if (next != OBJECT_KEYWORD.getValue()) {
keyword += " " + OBJECT_KEYWORD.getValue()
}
}
if (keyword.startsWith(prefix)/* use simple matching by prefix, not prefix matcher from completion*/ && parserFilter(keywordToken)) {
val element = LookupElementBuilder.create(KeywordLookupObject, keyword)
.bold()
@@ -79,7 +95,7 @@ object KeywordCompletion {
private class CommentFilter() : ElementFilter {
override fun isAcceptable(element : Any?, context : PsiElement?)
= (element is PsiElement) && JetPsiUtil.isInComment(element as PsiElement)
= (element is PsiElement) && JetPsiUtil.isInComment(element)
override fun isClassAcceptable(hintClass: Class<out Any?>)
= true
@@ -109,14 +125,14 @@ object KeywordCompletion {
is JetWithExpressionInitializer -> {
val initializer = _parent.getInitializer()
if (prevParent == initializer) {
return buildFilterWithContext("val v = ", initializer, position)
return buildFilterWithContext("val v = ", initializer!!, position)
}
}
is JetParameter -> {
val default = _parent.getDefaultValue()
if (prevParent == default) {
return buildFilterWithContext("val v = ", default, position)
return buildFilterWithContext("val v = ", default!!, position)
}
}
}
@@ -158,14 +174,14 @@ object KeywordCompletion {
return { keywordTokenType ->
val postfix = KEYWORD_TO_DUMMY_POSTFIX[keywordTokenType] ?: ""
val file = psiFactory.createFile(prefixText + keywordTokenType.getValue() + postfix)
val elementAt = file.findElementAt(prefixText.length)!!
val elementAt = file.findElementAt(prefixText.length())!!
when {
!elementAt.getNode()!!.getElementType().matchesKeyword(keywordTokenType) -> false
elementAt.getNonStrictParentOfType<PsiErrorElement>() != null -> false
elementAt.prevLeafSkipWhitespacesAndComments() is PsiErrorElement -> false
elementAt.prevLeaf { it !is PsiWhiteSpace && it !is PsiComment } is PsiErrorElement -> false
else -> true
}
@@ -194,14 +210,14 @@ object KeywordCompletion {
while (child != position) {
if (child is JetDeclaration) {
if (child == prevDeclaration) {
builder.appendReducedText(child!!)
builder.appendReducedText(child)
}
}
else {
builder.append(child!!.getText())
}
child = child!!.getNextSibling()
child = child.getNextSibling()
}
}
@@ -16,17 +16,19 @@
package org.jetbrains.kotlin.idea.completion
import com.intellij.codeInsight.lookup.CharFilter
import com.intellij.codeInsight.lookup.Lookup
import com.intellij.codeInsight.lookup.CharFilter.Result
import org.jetbrains.kotlin.psi.JetFile
import com.intellij.openapi.util.Key
import com.intellij.codeInsight.completion.CompletionService
import com.intellij.codeInsight.completion.CompletionProgressIndicator
import com.intellij.codeInsight.completion.CompletionService
import com.intellij.codeInsight.lookup.CharFilter
import com.intellij.codeInsight.lookup.CharFilter.Result
import com.intellij.codeInsight.lookup.Lookup
import com.intellij.openapi.util.Key
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.psi.psiUtil.prevLeafSkipWhitespacesAndComments
import com.intellij.psi.PsiWhiteSpace
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.psi.JetFunctionLiteral
import org.jetbrains.kotlin.psi.psiUtil.prevLeaf
public class KotlinCompletionCharFilter() : CharFilter() {
companion object {
@@ -86,9 +88,9 @@ public class KotlinCompletionCharFilter() : CharFilter() {
}
private fun isInFunctionLiteralStart(position: PsiElement): Boolean {
var prev = position.prevLeafSkipWhitespacesAndComments()
var prev = position.prevLeaf { it !is PsiWhiteSpace && it !is PsiComment }
if (prev?.getNode()?.getElementType() == JetTokens.LPAR) {
prev = prev?.prevLeafSkipWhitespacesAndComments()
prev = prev?.prevLeaf { it !is PsiWhiteSpace && it !is PsiComment }
}
if (prev?.getNode()?.getElementType() != JetTokens.LBRACE) return false
val functionLiteral = prev!!.getParent() as? JetFunctionLiteral ?: return false
@@ -39,7 +39,7 @@ public object KotlinKeywordInsertHandler : InsertHandler<LookupElement> {
FILE_KEYWORD,
CATCH_KEYWORD,
FINALLY_KEYWORD,
DYNAMIC_KEYWORD).map { it.getValue() }
DYNAMIC_KEYWORD).map { it.getValue() } + "companion object"
override fun handleInsert(context: InsertionContext, item: LookupElement) {
val keyword = item.getLookupString()
@@ -0,0 +1,7 @@
class C {
compa<caret>object {
}
}
// ELEMENT: "companion"
@@ -0,0 +1,7 @@
class C {
companion <caret>object {
}
}
// ELEMENT: "companion"
@@ -0,0 +1,3 @@
class C {
compa<caret>
}
@@ -0,0 +1,3 @@
class C {
companion object<caret>
}
@@ -34,5 +34,5 @@ class MouseMovedEventArgs
// EXIST: constructor
// EXIST: init
/*why?*/
// EXIST: companion
// EXIST: companion object
// NOTHING_ELSE: true
@@ -37,6 +37,6 @@ class B {
// EXIST: var
// EXIST: vararg
/*why?*/
// EXIST: companion
// EXIST: companion object
/*TODO*/
// NOTHING_ELSE: true
@@ -37,5 +37,5 @@ class A {
// EXIST: constructor
// EXIST: init
/*why?*/
// EXIST: companion
// EXIST: companion object
// NOTHING_ELSE: true
@@ -0,0 +1,6 @@
class TestClass {
<caret>
object O {}
}
// EXIST: "companion object"
@@ -37,6 +37,6 @@ var a : Int
// EXIST: var
// EXIST: vararg
/*why?*/
// EXIST: companion
// EXIST: companion object
/*TODO*/
// NOTHING_ELSE: true
@@ -35,5 +35,5 @@ public class Test {
// EXIST: constructor
// EXIST: init
/*why?*/
// EXIST: companion
// EXIST: companion object
// NOTHING_ELSE: true
@@ -29,5 +29,5 @@ class TestClass {
/*why?*/
// EXIST: constructor
// EXIST: init
// EXIST: companion
// EXIST: companion object
// NOTHING_ELSE: true
@@ -28,6 +28,6 @@ package Test
// EXIST: var
// EXIST: vararg
/*why?*/
// EXIST: companion
// EXIST: companion object
/*TODO*/
// NOTHING_ELSE: true
@@ -33,5 +33,5 @@ class Some {
/*why?*/
// EXIST: constructor
// EXIST: init
// EXIST: companion
// EXIST: companion object
// NOTHING_ELSE: true
@@ -33,5 +33,5 @@ class Some {
/*why?*/
// EXIST: constructor
// EXIST: init
// EXIST: companion
// EXIST: companion object
// NOTHING_ELSE: true
@@ -35,5 +35,5 @@ class Some {
/*why?*/
// EXIST: constructor
// EXIST: init
// EXIST: companion
// EXIST: companion object
// NOTHING_ELSE: true
@@ -27,6 +27,6 @@
// EXIST: var
// EXIST: vararg
/*why?*/
// EXIST: companion
// EXIST: companion object
/*TODO*/
// NOTHING_ELSE: true
@@ -95,6 +95,12 @@ public class KeywordCompletionTestGenerated extends AbstractKeywordCompletionTes
doTest(fileName);
}
@TestMetadata("CompanionObjectBeforeObject.kt")
public void testCompanionObjectBeforeObject() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/keywords/CompanionObjectBeforeObject.kt");
doTest(fileName);
}
@TestMetadata("ContinueWithLabel.kt")
public void testContinueWithLabel() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/keywords/ContinueWithLabel.kt");
@@ -31,6 +31,12 @@ import java.util.regex.Pattern;
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class KeywordCompletionHandlerTestGenerated extends AbstractKeywordCompletionHandlerTest {
@TestMetadata("AddCompanionToObject.kt")
public void testAddCompanionToObject() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/keywords/AddCompanionToObject.kt");
doTest(fileName);
}
public void testAllFilesPresentInKeywords() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/handlers/keywords"), Pattern.compile("^(.+)\\.kt$"), true);
}
@@ -41,6 +47,12 @@ public class KeywordCompletionHandlerTestGenerated extends AbstractKeywordComple
doTest(fileName);
}
@TestMetadata("CompanionObject.kt")
public void testCompanionObject() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/keywords/CompanionObject.kt");
doTest(fileName);
}
@TestMetadata("FileKeyword.kt")
public void testFileKeyword() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/keywords/FileKeyword.kt");